// Package operations contains read-side projections and operational observability. package operations import ( "encoding/json" "fmt" "orchestra/internal/domain" "os/exec" "strings" "time" ) type Brief struct { From, To time.Time `json:"from"` Completed int `json:"completed"` Failed int `json:"failed"` Blocked int `json:"blocked"` NeedsAttention []domain.Event `json:"needs_attention"` Quota map[string]float64 `json:"quota_consumed"` Git GitSync `json:"git_sync"` } type GitSync struct { Branch, Head, Status string `json:"branch" json:"head" json:"status"` } // BuildBrief folds only events in [from,to]. It is deliberately read-only. func BuildBrief(events []domain.Event, from, to time.Time, git GitSync) Brief { b := Brief{From: from, To: to, Quota: map[string]float64{}, Git: git} for _, e := range events { if e.At.Before(from) || e.At.After(to) { continue } var p map[string]any _ = json.Unmarshal(e.Payload, &p) switch e.Type { case "TaskCompleted": b.Completed++ case "TaskFailed": b.Failed++ b.NeedsAttention = append(b.NeedsAttention, e) case "TaskBlocked": b.Blocked++ b.NeedsAttention = append(b.NeedsAttention, e) case "ApprovalRequested": b.NeedsAttention = append(b.NeedsAttention, e) case "QuotaReported": if h, ok := p["harness_id"].(string); ok { if n, ok := p["consumed"].(float64); ok { b.Quota[h] += n } } } } return b } // GitState reports local checkout state for the brief. Git failures are visible, // never silently interpreted as synchronized. func GitState(dir string) GitSync { g := GitSync{Status: "unavailable"} run := func(args ...string) string { out, err := exec.Command("git", append([]string{"-C", dir}, args...)...).Output() if err != nil { return "" } return strings.TrimSpace(string(out)) } g.Branch, g.Head = run("branch", "--show-current"), run("rev-parse", "HEAD") if s := run("status", "--porcelain"); s != "" { g.Status = "dirty" } else if g.Head != "" { g.Status = "clean" } if g.Head == "" { g.Status = fmt.Sprintf("git unavailable (%s)", dir) } return g }