Implements deterministic feature lifecycle management for agent-driven
development. Agents use the CLI in pods; operators control via REST API.
Library (internal/sdlc/):
- Feature lifecycle with 10 phases (draft → released)
- Classifier engine with priority-ordered rules
- Artifact tracking with approval workflow
- Task management within features
- YAML-based state persistence
CLI (cmd/sdlc/):
- init, state, next, feature, artifact, task, query commands
- --json flag for machine-readable output
- Runs inside project pods
API (21 endpoints under /projects/{id}/sdlc/):
- State: GET /state, GET /next
- Features: CRUD + transition/block/unblock
- Artifacts: approve/reject per type
- Tasks: add/start/complete/block
- Queries: blocked/ready/needs-approval
Architecture:
- Port: SDLCExecutor interface (internal/port/)
- Adapter: kubectl exec into pods (internal/adapter/kubernetes/)
- Service: pod resolution + logging (internal/service/)
- Handlers: 5 files under 500-line limit (internal/handlers/)
Also includes template upgrades (chassis framework, UI components,
OpenAPI helpers, backend/frontend guides) and component improvements.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
100 lines
2.4 KiB
Go
100 lines
2.4 KiB
Go
package sdlc
|
|
|
|
import (
|
|
"path/filepath"
|
|
"regexp"
|
|
)
|
|
|
|
const (
|
|
// SDLCDir is the root directory name for SDLC state.
|
|
SDLCDir = ".sdlc"
|
|
|
|
// StateFile is the filename for global state.
|
|
StateFile = "state.yaml"
|
|
|
|
// ConfigFile is the filename for project config.
|
|
ConfigFile = "config.yaml"
|
|
|
|
// FeaturesDir holds per-feature subdirectories.
|
|
FeaturesDir = "features"
|
|
|
|
// PatternsDir holds pattern definitions.
|
|
PatternsDir = "patterns"
|
|
|
|
// AuditsDir holds audit reports.
|
|
AuditsDir = "audits"
|
|
|
|
// BranchesDir holds branch tracking files.
|
|
BranchesDir = "branches"
|
|
|
|
// ArchivesDir holds archived (released) features.
|
|
ArchivesDir = "archives"
|
|
|
|
// RoadmapDir holds roadmap documents.
|
|
RoadmapDir = "roadmap"
|
|
|
|
// ManifestFile is the per-feature metadata file.
|
|
ManifestFile = "manifest.yaml"
|
|
)
|
|
|
|
var slugPattern = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
|
|
|
|
// ValidateSlug returns nil if the slug is valid, ErrInvalidSlug otherwise.
|
|
func ValidateSlug(slug string) error {
|
|
if slug == "" || len(slug) > 64 || !slugPattern.MatchString(slug) {
|
|
return ErrInvalidSlug
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SDLCRoot returns the .sdlc directory path within a project root.
|
|
func SDLCRoot(root string) string {
|
|
return filepath.Join(root, SDLCDir)
|
|
}
|
|
|
|
// StatePath returns the path to state.yaml.
|
|
func StatePath(root string) string {
|
|
return filepath.Join(root, SDLCDir, StateFile)
|
|
}
|
|
|
|
// ConfigPath returns the path to config.yaml.
|
|
func ConfigPath(root string) string {
|
|
return filepath.Join(root, SDLCDir, ConfigFile)
|
|
}
|
|
|
|
// FeaturesDirPath returns the features/ directory path.
|
|
func FeaturesDirPath(root string) string {
|
|
return filepath.Join(root, SDLCDir, FeaturesDir)
|
|
}
|
|
|
|
// FeatureDir returns the directory for a specific feature.
|
|
func FeatureDir(root, slug string) string {
|
|
return filepath.Join(root, SDLCDir, FeaturesDir, slug)
|
|
}
|
|
|
|
// ManifestPath returns the manifest.yaml path for a feature.
|
|
func ManifestPath(root, slug string) string {
|
|
return filepath.Join(root, SDLCDir, FeaturesDir, slug, ManifestFile)
|
|
}
|
|
|
|
// ArtifactPath returns the file path for a feature artifact.
|
|
func ArtifactPath(root, slug string, artifactType ArtifactType) string {
|
|
filename := ArtifactFilename(artifactType)
|
|
if filename == "" {
|
|
return ""
|
|
}
|
|
return filepath.Join(root, SDLCDir, FeaturesDir, slug, filename)
|
|
}
|
|
|
|
// SubDirs returns all subdirectories that sdlc init creates.
|
|
func SubDirs() []string {
|
|
return []string{
|
|
FeaturesDir,
|
|
PatternsDir,
|
|
AuditsDir,
|
|
BranchesDir,
|
|
ArchivesDir,
|
|
RoadmapDir,
|
|
}
|
|
}
|