Function bodies 590 total
breakdown.dependenciesValid function · go · L114-L178 (65 LOC)cli/internal/projects/breakdown/guards.go
func dependenciesValid(p *state.Project) bool {
phase, exists := p.Phases["breakdown"]
if !exists {
return false
}
// Build adjacency list and valid task ID set
graph := make(map[string][]string)
taskIDs := make(map[string]bool)
for _, task := range phase.Tasks {
// Only validate completed tasks
if task.Status == "completed" {
taskIDs[task.Id] = true
// Extract dependencies from metadata
if deps := extractTaskDependencies(task); deps != nil {
graph[task.Id] = deps
}
}
}
// Check all dependencies point to valid task IDs
for _, deps := range graph {
for _, depID := range deps {
if !taskIDs[depID] {
return false // Invalid dependency reference
}
}
}
// Check for cycles using depth-first search
visited := make(map[string]bool)
recStack := make(map[string]bool)
var hasCycle func(string) bool
hasCycle = func(taskID string) bool {
visited[taskID] = true
recStack[taskID] = true
for _, depID := range graph[taskID] {
if !visitbreakdown.allWorkUnitsPublished function · go · L189-L222 (34 LOC)cli/internal/projects/breakdown/guards.go
func allWorkUnitsPublished(p *state.Project) bool {
phase, exists := p.Phases["breakdown"]
if !exists {
return false
}
hasCompleted := false
for _, task := range phase.Tasks {
// Only check completed tasks
if task.Status == "completed" {
hasCompleted = true
// Check metadata exists
if task.Metadata == nil {
return false
}
// Check published field exists and is true
publishedRaw, ok := task.Metadata["published"]
if !ok {
return false
}
published, ok := publishedRaw.(bool)
if !ok || !published {
return false
}
}
}
// Must have at least one completed task
return hasCompleted
}breakdown.countUnresolvedTasks function · go · L229-L242 (14 LOC)cli/internal/projects/breakdown/guards.go
func countUnresolvedTasks(p *state.Project) int {
phase, exists := p.Phases["breakdown"]
if !exists {
return 0
}
count := 0
for _, task := range phase.Tasks {
if task.Status != "completed" && task.Status != "abandoned" {
count++
}
}
return count
}breakdown.countUnpublishedTasks function · go · L248-L270 (23 LOC)cli/internal/projects/breakdown/guards.go
func countUnpublishedTasks(p *state.Project) int {
phase, exists := p.Phases["breakdown"]
if !exists {
return 0
}
count := 0
for _, task := range phase.Tasks {
if task.Status == "completed" {
// Count as unpublished if no metadata
if task.Metadata == nil {
count++
continue
}
// Count as unpublished if published field missing or not true
if published, ok := task.Metadata["published"].(bool); !ok || !published {
count++
}
}
}
return count
}breakdown.validateTaskForCompletion function · go · L282-L330 (49 LOC)cli/internal/projects/breakdown/guards.go
func validateTaskForCompletion(p *state.Project, taskID string) error {
phase, exists := p.Phases["breakdown"]
if !exists {
return fmt.Errorf("breakdown phase not found")
}
// Find task
var task *projschema.TaskState
for i := range phase.Tasks {
if phase.Tasks[i].Id == taskID {
task = &phase.Tasks[i]
break
}
}
if task == nil {
return fmt.Errorf("task %s not found", taskID)
}
// Check for metadata
if task.Metadata == nil {
return fmt.Errorf("task %s has no metadata - set artifact_path before completing", taskID)
}
// Check for artifact_path
artifactPathRaw, exists := task.Metadata["artifact_path"]
if !exists {
return fmt.Errorf("task %s has no artifact_path in metadata - link artifact to task before completing", taskID)
}
// Validate artifact_path is a non-empty string
artifactPath, ok := artifactPathRaw.(string)
if !ok || artifactPath == "" {
return fmt.Errorf("task %s has no artifact_path in metadata - link artifact to task before completing"breakdown.autoApproveArtifact function · go · L339-L385 (47 LOC)cli/internal/projects/breakdown/guards.go
func autoApproveArtifact(p *state.Project, taskID string) error {
phase, exists := p.Phases["breakdown"]
if !exists {
return fmt.Errorf("breakdown phase not found")
}
// Find task
var task *projschema.TaskState
for i := range phase.Tasks {
if phase.Tasks[i].Id == taskID {
task = &phase.Tasks[i]
break
}
}
if task == nil {
return fmt.Errorf("task %s not found", taskID)
}
// Get artifact_path from metadata
artifactPathRaw, exists := task.Metadata["artifact_path"]
if !exists {
return fmt.Errorf("task %s has invalid artifact_path in metadata", taskID)
}
artifactPath, ok := artifactPathRaw.(string)
if !ok || artifactPath == "" {
return fmt.Errorf("task %s has invalid artifact_path in metadata", taskID)
}
// Find and approve artifact
found := false
for i := range phase.Outputs {
if phase.Outputs[i].Path == artifactPath {
phase.Outputs[i].Approved = true
found = true
break
}
}
if !found {
return fmt.Errorf("artifact not found at %s", arbreakdown.generateOrchestratorPrompt function · go · L19-L29 (11 LOC)cli/internal/projects/breakdown/prompts.go
func generateOrchestratorPrompt(p *state.Project) string {
if p == nil {
return "Error: nil project provided to orchestrator prompt generator"
}
prompt, err := templates.Render(templatesFS, "templates/orchestrator.md", p)
if err != nil {
return fmt.Sprintf("Error rendering orchestrator prompt: %v", err)
}
return prompt
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
breakdown.generateDiscoveryPrompt function · go · L34-L66 (33 LOC)cli/internal/projects/breakdown/prompts.go
func generateDiscoveryPrompt(p *state.Project) string {
var buf strings.Builder
// Project header
buf.WriteString(fmt.Sprintf("# Breakdown: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n", p.Branch))
if p.Description != "" {
buf.WriteString(fmt.Sprintf("Description: %s\n", p.Description))
}
buf.WriteString("\n")
buf.WriteString("## Current State: Discovery\n\n")
// Breakdown phase info
phase, exists := p.Phases["breakdown"]
if !exists {
return "Error: breakdown phase not found"
}
// Show inputs if any
writeDiscoveryInputs(&buf, phase)
// Discovery status and readiness
writeDiscoveryStatus(&buf, p, phase)
// Render guidance from template
guidance, err := templates.Render(templatesFS, "templates/discovery.md", p)
if err != nil {
return buf.String() + fmt.Sprintf("\nError rendering template: %v", err)
}
buf.WriteString(guidance)
return buf.String()
}breakdown.writeDiscoveryInputs function · go · L69-L85 (17 LOC)cli/internal/projects/breakdown/prompts.go
func writeDiscoveryInputs(buf *strings.Builder, phase projschema.PhaseState) {
if len(phase.Inputs) == 0 {
return
}
buf.WriteString("### Input Materials\n\n")
buf.WriteString("Sources to analyze for breakdown:\n\n")
for _, input := range phase.Inputs {
fmt.Fprintf(buf, "- %s (%s)\n", input.Path, input.Type)
if input.Metadata != nil {
if desc, ok := input.Metadata["description"].(string); ok && desc != "" {
fmt.Fprintf(buf, " %s\n", desc)
}
}
}
buf.WriteString("\n")
}breakdown.writeDiscoveryStatus function · go · L88-L116 (29 LOC)cli/internal/projects/breakdown/prompts.go
func writeDiscoveryStatus(buf *strings.Builder, p *state.Project, phase projschema.PhaseState) {
buf.WriteString("### Discovery Status\n\n")
hasDiscovery := false
for _, artifact := range phase.Outputs {
if artifact.Type == "discovery" {
hasDiscovery = true
status := "[ ] Pending approval"
if artifact.Approved {
status = "[✓] Approved"
}
fmt.Fprintf(buf, "%s Discovery document: %s\n", status, artifact.Path)
}
}
if !hasDiscovery {
buf.WriteString("No discovery document created yet.\n\n")
} else {
buf.WriteString("\n")
}
// Advancement readiness
if hasApprovedDiscoveryDocument(p) {
buf.WriteString("✓ Discovery document approved!\n\n")
buf.WriteString("Ready to begin work unit identification. Run: `sow project advance`\n\n")
} else {
buf.WriteString("**Next steps**: Create and approve discovery document\n\n")
}
}breakdown.generateActivePrompt function · go · L123-L257 (135 LOC)cli/internal/projects/breakdown/prompts.go
func generateActivePrompt(p *state.Project) string {
var buf strings.Builder
// Project header
buf.WriteString(fmt.Sprintf("# Breakdown: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n", p.Branch))
if p.Description != "" {
buf.WriteString(fmt.Sprintf("Description: %s\n", p.Description))
}
buf.WriteString("\n")
// Current state
buf.WriteString("## Current State: Active Breakdown\n\n")
// Breakdown phase info
phase, exists := p.Phases["breakdown"]
if !exists {
return "Error: breakdown phase not found"
}
// Show inputs if any
if len(phase.Inputs) > 0 {
buf.WriteString("### Being Broken Down\n\n")
buf.WriteString("Sources being decomposed:\n\n")
for _, input := range phase.Inputs {
buf.WriteString(fmt.Sprintf("- %s\n", input.Path))
if input.Metadata != nil {
if desc, ok := input.Metadata["description"].(string); ok && desc != "" {
buf.WriteString(fmt.Sprintf(" %s\n", desc))
}
}
}
buf.WriteString("\n")
}
// Work units
bufbreakdown.generatePublishingPrompt function · go · L262-L356 (95 LOC)cli/internal/projects/breakdown/prompts.go
func generatePublishingPrompt(p *state.Project) string {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("# Breakdown: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n\n", p.Branch))
buf.WriteString("## Current State: Publishing\n\n")
buf.WriteString("All work units are approved and ready to be published as GitHub issues.\n\n")
buf.WriteString("**Let's review the publishing plan together before proceeding.**\n\n")
// Breakdown phase info
phase, exists := p.Phases["breakdown"]
if !exists {
return "Error: breakdown phase not found"
}
// Publishing status
buf.WriteString("### Publishing Status\n\n")
// Collect completed tasks
completed := []projschema.TaskState{}
for _, task := range phase.Tasks {
if task.Status == "completed" {
completed = append(completed, task)
}
}
// Count published vs unpublished
published := 0
unpublished := 0
for _, task := range completed {
if task.Metadata != nil {
if pub, ok := task.Metadata["published"].(boolbreakdown.getStatusIcon function · go · L360-L375 (16 LOC)cli/internal/projects/breakdown/prompts.go
func getStatusIcon(status string) string {
switch status {
case "completed":
return "[✓]"
case "abandoned":
return "[✗]"
case "needs_review":
return "[?]"
case "in_progress":
return "[~]"
case "pending":
return "[ ]"
default:
return "[ ]"
}
}design.NewDesignProjectConfig function · go · L18-L26 (9 LOC)cli/internal/projects/design/design.go
func NewDesignProjectConfig() *project.ProjectTypeConfig {
builder := project.NewProjectTypeConfigBuilder("design")
builder = configurePhases(builder)
builder = configureTransitions(builder)
builder = configureEventDeterminers(builder)
builder = configurePrompts(builder)
builder = builder.WithInitializer(initializeDesignProject)
return builder.Build()
}design.initializeDesignProject function · go · L38-L78 (41 LOC)cli/internal/projects/design/design.go
func initializeDesignProject(p *state.Project, initialInputs map[string][]projschema.ArtifactState) error {
now := p.Created_at
// Create design phase (starts active)
designInputs := []projschema.ArtifactState{}
if initialInputs != nil {
if phaseInputs, exists := initialInputs["design"]; exists {
designInputs = phaseInputs
}
}
p.Phases["design"] = projschema.PhaseState{
Status: "active",
Enabled: true,
Created_at: now,
Inputs: designInputs,
Outputs: []projschema.ArtifactState{},
Tasks: []projschema.TaskState{},
Metadata: make(map[string]interface{}),
}
// Create finalization phase (starts pending)
finalizationInputs := []projschema.ArtifactState{}
if initialInputs != nil {
if phaseInputs, exists := initialInputs["finalization"]; exists {
finalizationInputs = phaseInputs
}
}
p.Phases["finalization"] = projschema.PhaseState{
Status: "pending",
Enabled: false,
Created_at: now,
Inputs: finalizationInputs,
Repobility · MCP-ready · https://repobility.com
design.configurePhases function · go · L87-L103 (17 LOC)cli/internal/projects/design/design.go
func configurePhases(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
WithPhase("design",
project.WithStartState(project.State(Active)),
project.WithEndState(project.State(Active)),
project.WithOutputs("design", "adr", "architecture", "diagram", "spec"),
project.WithTasks(),
project.WithMetadataSchema(designMetadataSchema),
).
WithPhase("finalization",
project.WithStartState(project.State(Finalizing)),
project.WithEndState(project.State(Finalizing)),
project.WithOutputs("pr"),
project.WithTasks(),
project.WithMetadataSchema(finalizationMetadataSchema),
)
}design.configureTransitions function · go · L111-L144 (34 LOC)cli/internal/projects/design/design.go
func configureTransitions(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
// Set initial state to Active
SetInitialState(project.State(Active)).
// Transition 1: Active → Finalizing (inter-phase transition)
AddTransition(
project.State(Active),
project.State(Finalizing),
project.Event(EventCompleteDesign),
project.WithProjectGuard("all documents approved", func(p *state.Project) bool {
return allDocumentsApproved(p)
}),
project.WithProjectOnEntry(func(p *state.Project) error {
// Enable finalization phase
// Note: Phase status and timestamps are automatically managed by FireWithPhaseUpdates
phase := p.Phases["finalization"]
phase.Enabled = true
p.Phases["finalization"] = phase
return nil
}),
).
// Transition 2: Finalizing → Completed (terminal transition)
AddTransition(
project.State(Finalizing),
project.State(Completed),
project.Event(EventCompleteFinalization),
design.configureEventDeterminers function · go · L153-L161 (9 LOC)cli/internal/projects/design/design.go
func configureEventDeterminers(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
OnAdvance(project.State(Active), func(_ *state.Project) (project.Event, error) {
return project.Event(EventCompleteDesign), nil
}).
OnAdvance(project.State(Finalizing), func(_ *state.Project) (project.Event, error) {
return project.Event(EventCompleteFinalization), nil
})
}design.allDocumentsApproved function · go · L25-L47 (23 LOC)cli/internal/projects/design/guards.go
func allDocumentsApproved(p *state.Project) bool {
phase, exists := p.Phases["design"]
if !exists {
return false
}
if len(phase.Tasks) == 0 {
return false
}
hasCompleted := false
for _, task := range phase.Tasks {
if task.Status == "completed" {
hasCompleted = true
} else if task.Status != "abandoned" {
// Task is not completed or abandoned, so not all are resolved
return false
}
}
// Must have at least one completed task
return hasCompleted
}design.allFinalizationTasksComplete function · go · L58-L74 (17 LOC)cli/internal/projects/design/guards.go
func allFinalizationTasksComplete(p *state.Project) bool {
phase, exists := p.Phases["finalization"]
if !exists {
return false
}
if len(phase.Tasks) == 0 {
return false
}
for _, task := range phase.Tasks {
if task.Status != "completed" {
return false
}
}
return true
}design.countUnresolvedTasks function · go · L81-L94 (14 LOC)cli/internal/projects/design/guards.go
func countUnresolvedTasks(p *state.Project) int {
phase, exists := p.Phases["design"]
if !exists {
return 0
}
count := 0
for _, task := range phase.Tasks {
if task.Status != "completed" && task.Status != "abandoned" {
count++
}
}
return count
}design.validateTaskForCompletion function · go · L106-L154 (49 LOC)cli/internal/projects/design/guards.go
func validateTaskForCompletion(p *state.Project, taskID string) error {
phase, exists := p.Phases["design"]
if !exists {
return fmt.Errorf("design phase not found")
}
// Find task
var task *projschema.TaskState
for i := range phase.Tasks {
if phase.Tasks[i].Id == taskID {
task = &phase.Tasks[i]
break
}
}
if task == nil {
return fmt.Errorf("task %s not found", taskID)
}
// Check for metadata
if len(task.Metadata) == 0 {
return fmt.Errorf("task %s has no metadata - set artifact_path before completing", taskID)
}
// Check for artifact_path
artifactPathRaw, exists := task.Metadata["artifact_path"]
if !exists {
return fmt.Errorf("task %s has no artifact_path in metadata - link artifact to task before completing", taskID)
}
// Validate artifact_path is a string
artifactPath, ok := artifactPathRaw.(string)
if !ok || artifactPath == "" {
return fmt.Errorf("task %s has no artifact_path in metadata - link artifact to task before completing", taskID)
}
design.autoApproveArtifact function · go · L163-L209 (47 LOC)cli/internal/projects/design/guards.go
func autoApproveArtifact(p *state.Project, taskID string) error {
phase, exists := p.Phases["design"]
if !exists {
return fmt.Errorf("design phase not found")
}
// Find task
var task *projschema.TaskState
for i := range phase.Tasks {
if phase.Tasks[i].Id == taskID {
task = &phase.Tasks[i]
break
}
}
if task == nil {
return fmt.Errorf("task %s not found", taskID)
}
// Get artifact_path from metadata
artifactPathRaw, exists := task.Metadata["artifact_path"]
if !exists {
return fmt.Errorf("task %s has invalid artifact_path in metadata", taskID)
}
artifactPath, ok := artifactPathRaw.(string)
if !ok || artifactPath == "" {
return fmt.Errorf("task %s has invalid artifact_path in metadata", taskID)
}
// Find and approve artifact
found := false
for i := range phase.Outputs {
if phase.Outputs[i].Path == artifactPath {
phase.Outputs[i].Approved = true
found = true
break
}
}
if !found {
return fmt.Errorf("artifact not found at %s", artifactAll rows scored by the Repobility analyzer (https://repobility.com)
design.configurePrompts function · go · L19-L24 (6 LOC)cli/internal/projects/design/prompts.go
func configurePrompts(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
WithOrchestratorPrompt(generateOrchestratorPrompt).
WithPrompt(project.State(Active), generateActivePrompt).
WithPrompt(project.State(Finalizing), generateFinalizingPrompt)
}design.generateOrchestratorPrompt function · go · L29-L39 (11 LOC)cli/internal/projects/design/prompts.go
func generateOrchestratorPrompt(p *state.Project) string {
if p == nil {
return "Error: nil project provided to orchestrator prompt generator"
}
prompt, err := templates.Render(templatesFS, "templates/orchestrator.md", p)
if err != nil {
return fmt.Sprintf("Error rendering orchestrator prompt: %v", err)
}
return prompt
}design.generateActivePrompt function · go · L44-L152 (109 LOC)cli/internal/projects/design/prompts.go
func generateActivePrompt(p *state.Project) string {
var buf strings.Builder
// Project header
buf.WriteString(fmt.Sprintf("# Design: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n", p.Branch))
if p.Description != "" {
buf.WriteString(fmt.Sprintf("Description: %s\n", p.Description))
}
buf.WriteString("\n")
// Current state
buf.WriteString("## Current State: Active Design\n\n")
// Design phase info
phase, exists := p.Phases["design"]
if !exists {
return "Error: design phase not found"
}
// Show inputs if any
if len(phase.Inputs) > 0 {
buf.WriteString("### Design Inputs\n\n")
buf.WriteString("Sources informing this design:\n\n")
for _, input := range phase.Inputs {
buf.WriteString(fmt.Sprintf("- %s\n", input.Path))
if input.Metadata != nil {
if desc, ok := input.Metadata["description"].(string); ok && desc != "" {
buf.WriteString(fmt.Sprintf(" %s\n", desc))
}
}
}
buf.WriteString("\n")
}
// Document tasks
buf.WriteStridesign.generateFinalizingPrompt function · go · L157-L196 (40 LOC)cli/internal/projects/design/prompts.go
func generateFinalizingPrompt(p *state.Project) string {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("# Design: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n\n", p.Branch))
buf.WriteString("## Current State: Finalizing\n\n")
buf.WriteString("All documents approved. Finalizing design by moving artifacts, creating PR, and cleaning up.\n\n")
// Finalization tasks
phase, exists := p.Phases["finalization"]
if !exists {
return "Error: finalization phase not found"
}
buf.WriteString("### Finalization Tasks\n\n")
for _, task := range phase.Tasks {
status := "[ ]"
if task.Status == "completed" {
status = "[✓]"
}
buf.WriteString(fmt.Sprintf("%s %s\n", status, task.Name))
}
buf.WriteString("\n")
// Advancement readiness
if allFinalizationTasksComplete(p) {
buf.WriteString("✓ All finalization tasks complete!\n\n")
buf.WriteString("Ready to complete design. Run: `sow project advance`\n\n")
}
// Render additional guidance from template
guidesign.getStatusIcon function · go · L200-L215 (16 LOC)cli/internal/projects/design/prompts.go
func getStatusIcon(status string) string {
switch status {
case "completed":
return "[✓]"
case "abandoned":
return "[✗]"
case "needs_review":
return "[?]"
case "in_progress":
return "[~]"
case "pending":
return "[ ]"
default:
return "[ ]"
}
}exploration.NewExplorationProjectConfig function · go · L15-L23 (9 LOC)cli/internal/projects/exploration/exploration.go
func NewExplorationProjectConfig() *project.ProjectTypeConfig {
builder := project.NewProjectTypeConfigBuilder("exploration")
builder = configurePhases(builder)
builder = configureTransitions(builder)
builder = configureEventDeterminers(builder)
builder = configurePrompts(builder)
builder = builder.WithInitializer(initializeExplorationProject)
return builder.Build()
}exploration.initializeExplorationProject function · go · L35-L75 (41 LOC)cli/internal/projects/exploration/exploration.go
func initializeExplorationProject(p *state.Project, initialInputs map[string][]projschema.ArtifactState) error {
now := p.Created_at
// Create exploration phase (starts active)
explorationInputs := []projschema.ArtifactState{}
if initialInputs != nil {
if phaseInputs, exists := initialInputs["exploration"]; exists {
explorationInputs = phaseInputs
}
}
p.Phases["exploration"] = projschema.PhaseState{
Status: "active",
Enabled: true,
Created_at: now,
Inputs: explorationInputs,
Outputs: []projschema.ArtifactState{},
Tasks: []projschema.TaskState{},
Metadata: make(map[string]interface{}),
}
// Create finalization phase (starts pending)
finalizationInputs := []projschema.ArtifactState{}
if initialInputs != nil {
if phaseInputs, exists := initialInputs["finalization"]; exists {
finalizationInputs = phaseInputs
}
}
p.Phases["finalization"] = projschema.PhaseState{
Status: "pending",
Enabled: false,
Created_at: now,
exploration.configurePhases function · go · L82-L97 (16 LOC)cli/internal/projects/exploration/exploration.go
func configurePhases(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
WithPhase("exploration",
project.WithStartState(project.State(Active)),
project.WithEndState(project.State(Summarizing)),
project.WithOutputs("summary", "findings"),
project.WithTasks(),
project.WithMetadataSchema(explorationMetadataSchema),
).
WithPhase("finalization",
project.WithStartState(project.State(Finalizing)),
project.WithEndState(project.State(Finalizing)),
project.WithOutputs("pr"),
project.WithMetadataSchema(finalizationMetadataSchema),
)
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
exploration.configureTransitions function · go · L104-L154 (51 LOC)cli/internal/projects/exploration/exploration.go
func configureTransitions(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
// Set initial state to Active
SetInitialState(project.State(Active)).
// Transition 1: Active → Summarizing (intra-phase transition)
AddTransition(
project.State(Active),
project.State(Summarizing),
project.Event(EventBeginSummarizing),
project.WithProjectGuard("all tasks resolved", func(p *state.Project) bool {
return allTasksResolved(p)
}),
project.WithProjectOnEntry(func(p *state.Project) error {
// Update exploration phase status to "summarizing"
phase := p.Phases["exploration"]
phase.Status = "summarizing"
p.Phases["exploration"] = phase
return nil
}),
).
// Transition 2: Summarizing → Finalizing (inter-phase transition)
AddTransition(
project.State(Summarizing),
project.State(Finalizing),
project.Event(EventCompleteSummarizing),
project.WithProjectGuard("all summaries approved", func(p *exploration.configureEventDeterminers function · go · L162-L173 (12 LOC)cli/internal/projects/exploration/exploration.go
func configureEventDeterminers(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
OnAdvance(project.State(Active), func(_ *state.Project) (project.Event, error) {
return project.Event(EventBeginSummarizing), nil
}).
OnAdvance(project.State(Summarizing), func(_ *state.Project) (project.Event, error) {
return project.Event(EventCompleteSummarizing), nil
}).
OnAdvance(project.State(Finalizing), func(_ *state.Project) (project.Event, error) {
return project.Event(EventCompleteFinalization), nil
})
}exploration.allTasksResolved function · go · L20-L36 (17 LOC)cli/internal/projects/exploration/guards.go
func allTasksResolved(p *state.Project) bool {
phase, exists := p.Phases["exploration"]
if !exists {
return false
}
if len(phase.Tasks) == 0 {
return false
}
for _, task := range phase.Tasks {
if task.Status != "completed" && task.Status != "abandoned" {
return false
}
}
return true
}exploration.allSummariesApproved function · go · L47-L74 (28 LOC)cli/internal/projects/exploration/guards.go
func allSummariesApproved(p *state.Project) bool {
phase, exists := p.Phases["exploration"]
if !exists {
return false
}
// Collect all summary artifacts
summaries := []projschema.ArtifactState{}
for _, artifact := range phase.Outputs {
if artifact.Type == "summary" {
summaries = append(summaries, artifact)
}
}
// Must have at least one summary
if len(summaries) == 0 {
return false
}
// All summaries must be approved
for _, summary := range summaries {
if !summary.Approved {
return false
}
}
return true
}exploration.allFinalizationTasksComplete function · go · L85-L101 (17 LOC)cli/internal/projects/exploration/guards.go
func allFinalizationTasksComplete(p *state.Project) bool {
phase, exists := p.Phases["finalization"]
if !exists {
return false
}
if len(phase.Tasks) == 0 {
return false
}
for _, task := range phase.Tasks {
if task.Status != "completed" {
return false
}
}
return true
}exploration.countUnresolvedTasks function · go · L106-L119 (14 LOC)cli/internal/projects/exploration/guards.go
func countUnresolvedTasks(p *state.Project) int {
phase, exists := p.Phases["exploration"]
if !exists {
return 0
}
count := 0
for _, task := range phase.Tasks {
if task.Status != "completed" && task.Status != "abandoned" {
count++
}
}
return count
}exploration.countUnapprovedSummaries function · go · L124-L137 (14 LOC)cli/internal/projects/exploration/guards.go
func countUnapprovedSummaries(p *state.Project) int {
phase, exists := p.Phases["exploration"]
if !exists {
return 0
}
count := 0
for _, artifact := range phase.Outputs {
if artifact.Type == "summary" && !artifact.Approved {
count++
}
}
return count
}exploration.configurePrompts function · go · L19-L25 (7 LOC)cli/internal/projects/exploration/prompts.go
func configurePrompts(builder *project.ProjectTypeConfigBuilder) *project.ProjectTypeConfigBuilder {
return builder.
WithOrchestratorPrompt(generateOrchestratorPrompt).
WithPrompt(project.State(Active), generateActivePrompt).
WithPrompt(project.State(Summarizing), generateSummarizingPrompt).
WithPrompt(project.State(Finalizing), generateFinalizingPrompt)
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
exploration.generateOrchestratorPrompt function · go · L29-L35 (7 LOC)cli/internal/projects/exploration/prompts.go
func generateOrchestratorPrompt(p *state.Project) string {
prompt, err := templates.Render(templatesFS, "templates/orchestrator.md", p)
if err != nil {
return fmt.Sprintf("Error rendering orchestrator prompt: %v", err)
}
return prompt
}exploration.generateActivePrompt function · go · L39-L112 (74 LOC)cli/internal/projects/exploration/prompts.go
func generateActivePrompt(p *state.Project) string {
var buf strings.Builder
// Project header
buf.WriteString(fmt.Sprintf("# Exploration: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n", p.Branch))
if p.Description != "" {
buf.WriteString(fmt.Sprintf("Description: %s\n", p.Description))
}
buf.WriteString("\n")
// Current state
buf.WriteString("## Current State: Active Research\n\n")
// Research topics summary
phase, exists := p.Phases["exploration"]
if !exists {
return "Error: exploration phase not found"
}
if len(phase.Tasks) == 0 {
buf.WriteString("No research topics identified yet.\n\n")
buf.WriteString("**Next steps**: Create research topics to investigate.\n\n")
} else {
// Count task statuses
pending := 0
inProgress := 0
completed := 0
abandoned := 0
for _, task := range phase.Tasks {
switch task.Status {
case "pending":
pending++
case "in_progress":
inProgress++
case "completed":
completed++
case "abaexploration.generateSummarizingPrompt function · go · L116-L176 (61 LOC)cli/internal/projects/exploration/prompts.go
func generateSummarizingPrompt(p *state.Project) string {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("# Exploration: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n\n", p.Branch))
buf.WriteString("## Current State: Summarizing Findings\n\n")
buf.WriteString("All research topics are resolved. Create comprehensive summary document(s) synthesizing findings.\n\n")
// Research completed summary
phase, exists := p.Phases["exploration"]
if !exists {
return "Error: exploration phase not found"
}
completed := []projschema.TaskState{}
abandoned := []projschema.TaskState{}
for _, task := range phase.Tasks {
switch task.Status {
case "completed":
completed = append(completed, task)
case "abandoned":
abandoned = append(abandoned, task)
}
}
buf.WriteString(fmt.Sprintf("### Completed Topics: %d\n\n", len(completed)))
for _, task := range completed {
buf.WriteString(fmt.Sprintf("- %s\n", task.Name))
}
buf.WriteString("\n")
if len(abandoned)exploration.writeSummaryArtifactsSection function · go · L179-L212 (34 LOC)cli/internal/projects/exploration/prompts.go
func writeSummaryArtifactsSection(buf *strings.Builder, summaries []projschema.ArtifactState, p *state.Project) {
if len(summaries) == 0 {
buf.WriteString("No summaries created yet.\n\n")
buf.WriteString("**Next steps**: Create summary document(s) synthesizing findings.\n\n")
return
}
approvedCount := 0
for _, s := range summaries {
if s.Approved {
approvedCount++
}
}
fmt.Fprintf(buf, "Total: %d | Approved: %d\n\n", len(summaries), approvedCount)
for _, s := range summaries {
status := "Pending approval"
if s.Approved {
status = "✓ Approved"
}
fmt.Fprintf(buf, "- %s (%s)\n", s.Path, status)
}
buf.WriteString("\n")
// Advancement readiness
if allSummariesApproved(p) {
buf.WriteString("✓ All summaries approved!\n\n")
buf.WriteString("Ready to finalize. Run: `sow project advance`\n\n")
} else {
unapprovedCount := countUnapprovedSummaries(p)
fmt.Fprintf(buf, "**Next steps**: Review and approve %d summary document(s)\n\n", unapprovedCount)
}
exploration.generateFinalizingPrompt function · go · L216-L255 (40 LOC)cli/internal/projects/exploration/prompts.go
func generateFinalizingPrompt(p *state.Project) string {
var buf strings.Builder
buf.WriteString(fmt.Sprintf("# Exploration: %s\n", p.Name))
buf.WriteString(fmt.Sprintf("Branch: %s\n\n", p.Branch))
buf.WriteString("## Current State: Finalizing\n\n")
buf.WriteString("Summary approved. Finalizing exploration by moving artifacts, creating PR, and cleaning up.\n\n")
// Finalization tasks
phase, exists := p.Phases["finalization"]
if !exists {
return "Error: finalization phase not found"
}
buf.WriteString("### Finalization Tasks\n\n")
for _, task := range phase.Tasks {
status := "[ ]"
if task.Status == "completed" {
status = "[✓]"
}
buf.WriteString(fmt.Sprintf("%s %s\n", status, task.Name))
}
buf.WriteString("\n")
// Advancement readiness
if allFinalizationTasksComplete(p) {
buf.WriteString("✓ All finalization tasks complete!\n\n")
buf.WriteString("Ready to complete exploration. Run: `sow project advance`\n\n")
}
// Render additional guidance from tempstandard.phaseOutputApproved function · go · L12-L24 (13 LOC)cli/internal/projects/standard/guards.go
func phaseOutputApproved(p *state.Project, phaseName, outputType string) bool {
phase, exists := p.Phases[phaseName]
if !exists {
return false
}
for _, output := range phase.Outputs {
if output.Type == outputType && output.Approved {
return true
}
}
return false
}standard.allTaskDescriptionsApproved function · go · L33-L46 (14 LOC)cli/internal/projects/standard/guards.go
func allTaskDescriptionsApproved(p *state.Project) bool {
phase, exists := p.Phases["implementation"]
if !exists {
return false
}
// Check planning_approved metadata flag
if phase.Metadata == nil {
return false
}
approved, ok := phase.Metadata["planning_approved"].(bool)
return ok && approved
}standard.phaseMetadataBool function · go · L50-L71 (22 LOC)cli/internal/projects/standard/guards.go
func phaseMetadataBool(p *state.Project, phaseName, key string) bool {
phase, exists := p.Phases[phaseName]
if !exists {
return false
}
if phase.Metadata == nil {
return false
}
val, ok := phase.Metadata[key]
if !ok {
return false
}
boolVal, ok := val.(bool)
if !ok {
return false
}
return boolVal
}Repobility · MCP-ready · https://repobility.com
standard.allTasksComplete function · go · L75-L91 (17 LOC)cli/internal/projects/standard/guards.go
func allTasksComplete(p *state.Project) bool {
phase, exists := p.Phases["implementation"]
if !exists {
return false
}
if len(phase.Tasks) == 0 {
return false
}
for _, task := range phase.Tasks {
if task.Status != "completed" && task.Status != "abandoned" {
return false
}
}
return true
}standard.latestReviewApproved function · go · L95-L109 (15 LOC)cli/internal/projects/standard/guards.go
func latestReviewApproved(p *state.Project) bool {
phase, exists := p.Phases["review"]
if !exists {
return false
}
// Find latest review output by iterating backwards
for i := len(phase.Outputs) - 1; i >= 0; i-- {
if phase.Outputs[i].Type == "review" {
return phase.Outputs[i].Approved
}
}
return false
}standard.getReviewAssessment function · go · L139-L156 (18 LOC)cli/internal/projects/standard/guards.go
func getReviewAssessment(p *state.Project) string {
phase, exists := p.Phases["review"]
if !exists {
return ""
}
// Find latest approved review by iterating backwards
for i := len(phase.Outputs) - 1; i >= 0; i-- {
artifact := phase.Outputs[i]
if artifact.Type == "review" && artifact.Approved {
if assessment, ok := artifact.Metadata["assessment"].(string); ok {
return assessment // "pass" or "fail"
}
}
}
return "" // No approved review or assessment missing
}