feat(synthesis): Add synthesis step for convoy formulas (gt-v5s0j)
Implements the synthesis step that combines leg outputs into a final deliverable for convoy workflows: - `gt synthesis start <convoy-id>` - Start synthesis after verifying all legs are complete, collecting outputs, and slinging to polecat - `gt synthesis status <convoy-id>` - Show synthesis readiness and leg completion status - `gt synthesis close <convoy-id>` - Close convoy after synthesis Key features: - Collects leg outputs from formula-defined paths (e.g., findings.md) - Creates synthesis bead with combined context from all leg outputs - Integrates with formula.toml synthesis configuration - Provides TriggerSynthesisIfReady() for automated synthesis trigger - Adds SynthesisFields struct for tracking synthesis state in beads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
94
internal/cmd/synthesis_test.go
Normal file
94
internal/cmd/synthesis_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExpandOutputPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directory string
|
||||
pattern string
|
||||
reviewID string
|
||||
legID string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "basic expansion",
|
||||
directory: ".reviews/{{review_id}}",
|
||||
pattern: "{{leg.id}}-findings.md",
|
||||
reviewID: "abc123",
|
||||
legID: "security",
|
||||
want: ".reviews/abc123/security-findings.md",
|
||||
},
|
||||
{
|
||||
name: "no templates",
|
||||
directory: ".output",
|
||||
pattern: "results.md",
|
||||
reviewID: "xyz",
|
||||
legID: "test",
|
||||
want: ".output/results.md",
|
||||
},
|
||||
{
|
||||
name: "complex path",
|
||||
directory: "reviews/{{review_id}}/findings",
|
||||
pattern: "leg-{{leg.id}}-analysis.md",
|
||||
reviewID: "pr-123",
|
||||
legID: "performance",
|
||||
want: "reviews/pr-123/findings/leg-performance-analysis.md",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := expandOutputPath(tt.directory, tt.pattern, tt.reviewID, tt.legID)
|
||||
if got != tt.want {
|
||||
t.Errorf("expandOutputPath() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegOutput(t *testing.T) {
|
||||
// Test LegOutput struct
|
||||
output := LegOutput{
|
||||
LegID: "correctness",
|
||||
Title: "Correctness Review",
|
||||
Status: "closed",
|
||||
FilePath: "/tmp/findings.md",
|
||||
Content: "## Findings\n\nNo issues found.",
|
||||
HasFile: true,
|
||||
}
|
||||
|
||||
if output.LegID != "correctness" {
|
||||
t.Errorf("LegID = %q, want %q", output.LegID, "correctness")
|
||||
}
|
||||
|
||||
if output.Status != "closed" {
|
||||
t.Errorf("Status = %q, want %q", output.Status, "closed")
|
||||
}
|
||||
|
||||
if !output.HasFile {
|
||||
t.Error("HasFile should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvoyMeta(t *testing.T) {
|
||||
// Test ConvoyMeta struct
|
||||
meta := ConvoyMeta{
|
||||
ID: "hq-cv-abc",
|
||||
Title: "Code Review: PR #123",
|
||||
Status: "open",
|
||||
Formula: "code-review",
|
||||
ReviewID: "pr123",
|
||||
LegIssues: []string{"gt-leg1", "gt-leg2", "gt-leg3"},
|
||||
}
|
||||
|
||||
if meta.ID != "hq-cv-abc" {
|
||||
t.Errorf("ID = %q, want %q", meta.ID, "hq-cv-abc")
|
||||
}
|
||||
|
||||
if len(meta.LegIssues) != 3 {
|
||||
t.Errorf("len(LegIssues) = %d, want 3", len(meta.LegIssues))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user