← back to jmgilman__sow

Function bodies 590 total

All specs Real LLM only Function bodies
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 !visit
breakdown.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", ar
breakdown.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
	buf
breakdown.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"].(bool
breakdown.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", artifact
All 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.WriteStri
design.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
	gui
design.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 "aba
exploration.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 temp
standard.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
}
‹ prevpage 7 / 12next ›