Add branch lifecycle commands (branch, merge, archive) to the SDLC CLI. Introduce orchestrator handler and service for multi-step SDLC workflows. Expand skeleton template with 15 Claude commands covering the full feature lifecycle. Extend classifier rules, error types, and executor port for branch operations. Split rules.go and classifier_test.go to stay within 500-line limit. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
230 lines
5.5 KiB
Go
230 lines
5.5 KiB
Go
package sdlc
|
|
|
|
import "fmt"
|
|
|
|
// Execution phase rules: review, audit, QA, merge, archive.
|
|
|
|
func needsReviewRule() Rule {
|
|
return Rule{
|
|
ID: "needs-review",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseReview {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactReview)
|
|
return art == nil || art.Status == StatusPending
|
|
},
|
|
Action: ActionReviewCode,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/review-feature " + ctx.Feature.Slug
|
|
},
|
|
OutputPath: func(ctx *EvalContext) string {
|
|
return ".sdlc/features/" + ctx.Feature.Slug + "/review.md"
|
|
},
|
|
}
|
|
}
|
|
|
|
func reviewHasIssuesRule() Rule {
|
|
return Rule{
|
|
ID: "review-has-issues",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseReview {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactReview)
|
|
return art != nil && art.Status == StatusNeedsFix
|
|
},
|
|
Action: ActionFixReviewIssues,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/fix-review-issues " + ctx.Feature.Slug
|
|
},
|
|
}
|
|
}
|
|
|
|
func reviewPassedRule() Rule {
|
|
return Rule{
|
|
ID: "review-passed",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseReview {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactReview)
|
|
return art != nil && art.Status == StatusPassed
|
|
},
|
|
Action: ActionTransition,
|
|
TransitionTo: PhaseAudit,
|
|
}
|
|
}
|
|
|
|
func needsAuditRule() Rule {
|
|
return Rule{
|
|
ID: "needs-audit",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseAudit {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactAudit)
|
|
return art == nil || art.Status == StatusPending
|
|
},
|
|
Action: ActionAuditCode,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/audit-feature " + ctx.Feature.Slug
|
|
},
|
|
OutputPath: func(ctx *EvalContext) string {
|
|
return ".sdlc/features/" + ctx.Feature.Slug + "/audit.md"
|
|
},
|
|
}
|
|
}
|
|
|
|
func auditHasIssuesRule() Rule {
|
|
return Rule{
|
|
ID: "audit-has-issues",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseAudit {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactAudit)
|
|
return art != nil && art.Status == StatusNeedsFix
|
|
},
|
|
Action: ActionRemediateAudit,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/remediate-audit " + ctx.Feature.Slug
|
|
},
|
|
}
|
|
}
|
|
|
|
func auditPassedRule() Rule {
|
|
return Rule{
|
|
ID: "audit-passed",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseAudit {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactAudit)
|
|
return art != nil && art.Status == StatusPassed
|
|
},
|
|
Action: ActionTransition,
|
|
TransitionTo: PhaseQA,
|
|
}
|
|
}
|
|
|
|
func needsQARule() Rule {
|
|
return Rule{
|
|
ID: "needs-qa",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseQA {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactQAResults)
|
|
return art == nil || art.Status == StatusPending
|
|
},
|
|
Action: ActionRunQA,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/run-qa " + ctx.Feature.Slug
|
|
},
|
|
OutputPath: func(ctx *EvalContext) string {
|
|
return ".sdlc/features/" + ctx.Feature.Slug + "/qa-results.md"
|
|
},
|
|
}
|
|
}
|
|
|
|
func qaHasFailuresRule() Rule {
|
|
return Rule{
|
|
ID: "qa-has-failures",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseQA {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactQAResults)
|
|
return art != nil && art.Status == StatusFailed
|
|
},
|
|
Action: ActionFixQAFailures,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/fix-qa-failures " + ctx.Feature.Slug
|
|
},
|
|
}
|
|
}
|
|
|
|
func qaPassedRule() Rule {
|
|
return Rule{
|
|
ID: "qa-passed",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseQA {
|
|
return false
|
|
}
|
|
art := ctx.Feature.GetArtifact(ArtifactQAResults)
|
|
return art != nil && art.Status == StatusPassed
|
|
},
|
|
Action: ActionTransition,
|
|
TransitionTo: PhaseMerge,
|
|
}
|
|
}
|
|
|
|
func needsMergeRule() Rule {
|
|
return Rule{
|
|
ID: "needs-merge",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
return ctx.Feature.Phase == PhaseMerge
|
|
},
|
|
Action: ActionMergeFeature,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/merge-feature " + ctx.Feature.Slug
|
|
},
|
|
}
|
|
}
|
|
|
|
func archiveFeatureRule() Rule {
|
|
return Rule{
|
|
ID: "archive-feature",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
return ctx.Feature.Phase == PhaseReleased
|
|
},
|
|
Action: ActionArchive,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
return "/archive-feature " + ctx.Feature.Slug
|
|
},
|
|
}
|
|
}
|
|
|
|
func implementNextTaskRule() Rule {
|
|
return Rule{
|
|
ID: "implement-next-task",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
if ctx.Feature.Phase != PhaseImplementation {
|
|
return false
|
|
}
|
|
next := NextTask(ctx.Feature.Tasks)
|
|
return next != nil
|
|
},
|
|
Action: ActionImplementTask,
|
|
NextCommand: func(ctx *EvalContext) string {
|
|
next := NextTask(ctx.Feature.Tasks)
|
|
if next == nil {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf("/implement-task %s %s", ctx.Feature.Slug, next.ID)
|
|
},
|
|
OutputPath: func(ctx *EvalContext) string {
|
|
return ".sdlc/features/" + ctx.Feature.Slug + "/tasks.md"
|
|
},
|
|
TaskID: func(ctx *EvalContext) string {
|
|
next := NextTask(ctx.Feature.Tasks)
|
|
if next == nil {
|
|
return ""
|
|
}
|
|
return next.ID
|
|
},
|
|
}
|
|
}
|
|
|
|
func implementationCompleteRule() Rule {
|
|
return Rule{
|
|
ID: "implementation-complete",
|
|
Condition: func(ctx *EvalContext) bool {
|
|
return ctx.Feature.Phase == PhaseImplementation && AllTasksComplete(ctx.Feature.Tasks)
|
|
},
|
|
Action: ActionTransition,
|
|
TransitionTo: PhaseReview,
|
|
}
|
|
}
|