// Package operations contains read-side projections and operational observability. package operations import ( "encoding/json" "fmt" "orchestra/internal/authz" "orchestra/internal/domain" "orchestra/internal/store" "os/exec" "strings" "time" ) type Brief struct { From time.Time `json:"from"` To time.Time `json:"to"` 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 string `json:"branch"` Head string `json:"head"` Status string `json:"status"` } type StandupItem struct { TaskID string `json:"task_id"` State domain.TaskState `json:"state"` Title string `json:"title,omitempty"` } // QuotaReceipt is the native usage receipt produced by a harness. Receipts // are additive: a task that crosses rotations contributes each rotation's // receipt to every window containing it. type QuotaReceipt struct { HarnessID string `json:"harness_id"` Consumed float64 `json:"consumed"` At time.Time `json:"at"` } // AggregateQuota sums native receipts in the requested window. It does not // de-duplicate rotations or use a cumulative session total. func AggregateQuota(events []domain.Event, from, to time.Time) map[string]float64 { out := map[string]float64{} for _, e := range events { if e.Type != "QuotaReported" || e.At.Before(from) || e.At.After(to) { continue } var r QuotaReceipt if json.Unmarshal(e.Payload, &r) == nil && r.HarnessID != "" && r.Consumed >= 0 { out[r.HarnessID] += r.Consumed } } return out } func StandupItems(tasks []domain.Task) []StandupItem { out := make([]StandupItem, 0) for _, t := range tasks { if t.State == domain.StateQueued || t.State == domain.StateLeased || t.State == domain.StateBlocked { out = append(out, StandupItem{t.ID, t.State, t.Title}) } } return out } // 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) } } b.Quota = AggregateQuota(events, from, to) return b } // GenerateStandupAdvisory creates a persisted, read-only recommendation. // Applying it is intentionally a separate approval-gated operation. func GenerateStandupAdvisory(s *store.Store, at time.Time) (domain.Event, error) { items := StandupItems(s.Tasks()) p, err := json.Marshal(map[string]any{"items": items, "generated_at": at.UTC()}) if err != nil { return domain.Event{}, err } e := domain.Event{ID: domain.NewID(), TaskID: "system", Type: "StandupAdvisory", Payload: p, At: at, Surface: string(authz.System)} return e, s.Append(e) } // ApplyAdvisory applies only approved title recommendations. Unknown or // malformed recommendations are ignored, while event conflicts are returned. func ApplyAdvisory(s *store.Store, advisoryID string) ([]domain.Event, error) { var advisory *domain.Event approved := false for _, e := range s.Events(0) { if e.ID == advisoryID && e.Type == "StandupAdvisory" { x := e advisory = &x } if e.Type == "ApprovalGranted" { var p struct { SubjectRef string `json:"subject_ref"` } _ = json.Unmarshal(e.Payload, &p) if p.SubjectRef == advisoryID { approved = true } } } if advisory == nil { return nil, fmt.Errorf("advisory %q not found", advisoryID) } if !approved { return nil, fmt.Errorf("advisory %q is not approved", advisoryID) } var p struct { Items []StandupItem `json:"items"` } if err := json.Unmarshal(advisory.Payload, &p); err != nil { return nil, err } var out []domain.Event for _, item := range p.Items { if item.TaskID == "" || item.Title == "" { continue } t, ok := s.Task(item.TaskID) if !ok || t.Title == item.Title { continue } b, _ := json.Marshal(map[string]any{"title": item.Title, "advisory_ref": advisoryID}) e := domain.Event{ID: domain.NewID(), TaskID: item.TaskID, Type: "TaskAmended", Version: t.Version + 1, Payload: b, Surface: string(authz.System)} if err := s.Append(e); err != nil { return out, err } out = append(out, e) } return out, nil } // 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 }