Brainstorm Backpressure + Approval Backstop Implementation Plan¶
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax. TDD throughout: failing test first, watch it fail, minimal code, watch it pass.
Goal: Make the operator's brainstorm activity per-repo and backpressured (hourly; skip a repo once it holds >= maxOpenProposals open unapproved agent proposals; else start one brainstorm), and add an approval backstop that recovers proposals whose approval webhook was missed.
Architecture: Extends internal/controller/projectscan.go's hourly runScans. brainstorm() becomes per-repo with an in-flight guard + a live-SCM proposal-count cap. A new approvalBackstop() step flips stuck AwaitingApproval proposal Tasks whose issue lost the approval label. New CRD field BrainstormActivity.MaxOpenProposals; new metrics.
Tech Stack: Go, controller-runtime, kubebuilder CRDs, Prometheus client, envtest (make test), gofmt + golangci-lint.
Spec: docs/superpowers/specs/2026-06-13-brainstorm-backpressure-approval-backstop-design.md
Key existing symbols (verified): - BrainstormActivity{Enabled, Schedule, MaxPerCycle, Sources} (api/v1alpha1/project_types.go:81). - CronActivity{MaxPerRepo, Schedule}; ScmSpec.ApprovalLabel (project_types.go). - ProposedIssueSpec{RepositoryRef, Title, Body, Kind}; TaskSpec.ProposedIssue; TaskSpec.Source{Number, IssueRef, ...}. - ConditionApprovalApproved = "ApprovalApproved"; phase "AwaitingApproval". - isTerminal(phase) = Succeeded||Failed; isActive(phase) = Planning||Running. - Helpers: scanReader, projectReposForScan, existingScanTasks, activityDue, createBrainstormTask(ctx,proj,*repo,goal,sources), hasLabel(labels,want), scanTaskLabels(c,activity,kind), r.Metrics.ScanItem(activity,outcome). - scm.SCMReader.ListOpenIssues(ctx,owner,repo) []IssueRef{Repo,Number,Title,Labels,UpdatedAt,IsPR}; GetIssue(ctx,owner,repo,number) IssueContent{Title,Body} (added earlier today). - scm.OwnerRepo(repoURL) (owner,repo,err); repo URL via repo.Spec.URL.
Task 1: CRD field BrainstormActivity.MaxOpenProposals¶
Files: - Modify: api/v1alpha1/project_types.go (BrainstormActivity struct) - Modify: config/crd/bases/*project*.yaml + charts/tatara-operator/crds/* (via make manifests) - Test: api/v1alpha1/project_types_test.go
- Step 1: Failing test in
project_types_test.go: - Step 2:
go test ./api/v1alpha1/ -run MaxOpenProposals-> FAIL (field undefined). - Step 3: add to
BrainstormActivity(afterMaxPerCycle):Leave// MaxOpenProposals caps open, unapproved agent proposals per repo; at or // above this the repo is skipped. Default 3. // +kubebuilder:default=3 // +optional MaxOpenProposals int `json:"maxOpenProposals,omitempty"`MaxPerCyclein place (retired: ignored by the new brainstorm; keep the field for API compat). - Step 4:
go test ./api/v1alpha1/ -run MaxOpenProposals-> PASS. Runmake manifeststo regen CRDs. - Step 5: commit
feat(crd): BrainstormActivity.maxOpenProposals (default 3).
Task 2: Metrics - open-proposals gauge + backstop counter¶
Files: - Modify: internal/obs/operator_metrics.go - Test: internal/obs/operator_metrics_test.go
- Step 1: Failing tests in
operator_metrics_test.go:func TestOpenProposalsGauge(t *testing.T) { reg := prometheus.NewRegistry() m := NewOperatorMetrics(reg) m.SetOpenProposals("o/r", 2) if got := testutil.ToFloat64(m.openProposals.WithLabelValues("o/r")); got != 2 { t.Fatalf("openProposals{o/r} = %v, want 2", got) } } func TestApprovalBackstopFlips(t *testing.T) { reg := prometheus.NewRegistry() m := NewOperatorMetrics(reg) m.ApprovalBackstopFlip() m.ApprovalBackstopFlip() if got := testutil.ToFloat64(m.approvalBackstopFlips); got != 2 { t.Fatalf("approvalBackstopFlips = %v, want 2", got) } } - Step 2:
go test ./internal/obs/ -run 'OpenProposals|ApprovalBackstop'-> FAIL (undefined). - Step 3: add fields to
OperatorMetricsstruct: construct inNewOperatorMetrics:register both in theopenProposals: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "operator_open_proposals", Help: "Open, unapproved agent-proposed issues per repo.", }, []string{"repo"}), approvalBackstopFlips: prometheus.NewCounter(prometheus.CounterOpts{ Name: "operator_approval_backstop_flips_total", Help: "Approvals recovered by the backstop after a missed webhook.", }),reg.MustRegister(...)list; add helpers: - Step 4:
go test ./internal/obs/ -run 'OpenProposals|ApprovalBackstop'-> PASS. - Step 5: commit
feat(obs): open_proposals gauge + approval_backstop_flips counter.
Task 3: Per-repo backpressured brainstorm¶
Files: - Modify: internal/controller/projectscan.go (brainstorm + its call in runScans) - Test: internal/controller/projectscan_run_test.go
Change brainstorm signature to take the reader + existing tasks (like mrScan/issueScan): func (r *ProjectReconciler) brainstorm(ctx, proj, reader scm.SCMReader, repos []Repository, existing []Task, act BrainstormActivity).
- Step 1: Failing tests in
projectscan_run_test.go(mirror the existing scan-test harness:newProjectReconciler-style ctor with a fakeReaderForwhoseListOpenIssuesreturns scriptedIssueRefs; helpers to create Project withScm.Cron.Brainstorm+Scm.ApprovalLabel="tatara/awaiting-approval", Repositories, and Tasks). Write:Assert by listing Tasks with label// under cap, no in-flight -> one brainstorm task per repo, targeting it func TestBrainstorm_UnderCap_CreatesOnePerRepo(t *testing.T) { /* 2 repos, 0 proposals each -> 2 brainstorm tasks, one per repo */ } // at/over cap -> skip that repo func TestBrainstorm_AtCap_SkipsRepo(t *testing.T) { /* repo with 3 open issues bearing approvalLabel -> no brainstorm task */ } // in-flight brainstorm -> skip even if under cap func TestBrainstorm_InFlight_SkipsRepo(t *testing.T) { /* pre-create a brainstorm Task (phase Planning) for repo -> no new task */ } // listing error for one repo does not block others func TestBrainstorm_ListErrorIsolatesRepo(t *testing.T) { /* reader errors for repo A, ok for repo B -> B gets a task */ }tatara.dev/activity=brainstorm(viascanTaskLabels) and checkingSpec.RepositoryRef. - Step 2:
go test ./internal/controller/ -run Brainstorm(envtest) -> FAIL. - Step 3: Implement. Replace
brainstormbody:Add the exact activity-label constant:func (r *ProjectReconciler) brainstorm(ctx context.Context, proj *tatarav1alpha1.Project, reader scm.SCMReader, repos []tatarav1alpha1.Repository, existing []tatarav1alpha1.Task, act tatarav1alpha1.BrainstormActivity) { l := log.FromContext(ctx) start := time.Now() cap := act.MaxOpenProposals if cap < 1 { cap = 3 } approvalLabel := "" if proj.Spec.Scm != nil { approvalLabel = proj.Spec.Scm.ApprovalLabel } created := 0 for i := range repos { repo := repos[i] slug := repoSlug(&repo) // owner/repo from repo.Spec.URL via scm.OwnerRepo; see helper below if slug == "" { continue } if brainstormInFlight(existing, repo.Name) { r.Metrics.ScanItem("brainstorm", "skipped_inflight") continue } backlog, err := r.proposalBacklog(ctx, reader, &repo, approvalLabel, existing) if err != nil { l.Info("brainstorm: backlog count failed (non-fatal)", "resource_id", proj.Name, "repo", repo.Name, "err", err.Error()) continue } r.Metrics.SetOpenProposals(slug, float64(backlog)) if backlog >= cap { r.Metrics.ScanItem("brainstorm", "skipped_cap") continue } goal := "Propose a single, well-defined issue for repo " + slug if _, err := r.createBrainstormTask(ctx, proj, &repo, goal, act.Sources); err != nil { l.Error(err, "scan: create brainstorm task", "resource_id", proj.Name, "repo", repo.Name) continue } r.Metrics.ScanItem("brainstorm", "picked") created++ } r.Metrics.ObserveScanDuration("brainstorm", time.Since(start).Seconds()) l.Info("brainstorm complete", "action", "scan_brainstorm", "resource_id", proj.Name, "picked", created, "duration_ms", time.Since(start).Milliseconds()) } // brainstormInFlight reports whether a non-terminal brainstorm Task targets repoName. func brainstormInFlight(existing []tatarav1alpha1.Task, repoName string) bool { for i := range existing { t := existing[i] if t.Labels["tatara.dev/activity"] == "brainstorm" && t.Spec.RepositoryRef == repoName && !isTerminal(t.Status.Phase) { return true } } return false } // proposalBacklog counts open, unapproved agent proposals for repo. When // approvalLabel is set it counts open non-PR issues bearing that label (live // ListOpenIssues). When empty it falls back to counting AwaitingApproval // proposal Tasks for the repo. func (r *ProjectReconciler) proposalBacklog(ctx context.Context, reader scm.SCMReader, repo *tatarav1alpha1.Repository, approvalLabel string, existing []tatarav1alpha1.Task) (int, error) { if approvalLabel == "" { n := 0 for i := range existing { t := existing[i] if t.Spec.RepositoryRef == repo.Name && t.Spec.ProposedIssue != nil && t.Status.Phase == "AwaitingApproval" { n++ } } return n, nil } owner, name, err := scm.OwnerRepo(repo.Spec.URL) if err != nil { return 0, err } issues, err := reader.ListOpenIssues(ctx, owner, name) if err != nil { return 0, err } n := 0 for _, iss := range issues { if !iss.IsPR && hasLabel(iss.Labels, approvalLabel) { n++ } } return n, nil }scanTaskLabelsuses a label key for activity; reuse the SAME key (find it inscanTaskLabels, e.g.labelActivity). Replace"tatara.dev/activity"literals with that constant. AddrepoSlug(&repo)helper usingscm.OwnerRepo(repo.Spec.URL)returningowner+"/"+nameor""on error (or reuse an existing slug helper if present - grepOwnerRepo(in projectscan.go first). - Step 4: update the
runScansbrainstorm call tor.brainstorm(ctx, proj, reader, repos, existing, cronSpec.Brainstorm).go test ./internal/controller/ -run Brainstorm-> PASS;go build ./.... - Step 5: commit
feat(scan): per-repo backpressured brainstorm (cap maxOpenProposals, in-flight guard).
Task 4: Approval backstop¶
Files: - Modify: internal/controller/projectscan.go (new approvalBackstop + call in runScans) - Test: internal/controller/projectscan_run_test.go
- Step 1: Failing tests:
// approved (label gone) + open + no impl running -> ApprovalApproved flipped True func TestApprovalBackstop_FlipsStuckApproved(t *testing.T) { /* AwaitingApproval proposal Task; fake GetIssue/ListOpenIssues report the issue open WITHOUT approvalLabel -> condition flipped */ } // label still present -> no flip func TestApprovalBackstop_NotApproved_NoOp(t *testing.T) {} // implementation already running for the issue -> no flip func TestApprovalBackstop_ImplRunning_NoOp(t *testing.T) {} - Step 2:
go test ./internal/controller/ -run ApprovalBackstop-> FAIL. - Step 3: Implement: Note:
// approvalBackstop recovers proposals approved on the SCM (approval label // removed) whose ApprovalApproved condition was never flipped (missed webhook). func (r *ProjectReconciler) approvalBackstop(ctx context.Context, proj *tatarav1alpha1.Project, reader scm.SCMReader, repos []tatarav1alpha1.Repository, existing []tatarav1alpha1.Task) { l := log.FromContext(ctx) if proj.Spec.Scm == nil || proj.Spec.Scm.ApprovalLabel == "" { return } approvalLabel := proj.Spec.Scm.ApprovalLabel for i := range existing { t := existing[i] if t.Spec.ProposedIssue == nil || t.Status.Phase != "AwaitingApproval" || t.Spec.Source == nil { continue } if apimeta.IsStatusConditionTrue(t.Status.Conditions, tatarav1alpha1.ConditionApprovalApproved) { continue } if implRunningForIssue(existing, t.Spec.Source.IssueRef, t.Name) { continue } repo := repoByName(repos, t.Spec.RepositoryRef) if repo == nil { continue } owner, name, err := scm.OwnerRepo(repo.Spec.URL) if err != nil { continue } issues, err := reader.ListOpenIssues(ctx, owner, name) if err != nil { l.Info("approvalBackstop: list issues failed (non-fatal)", "resource_id", proj.Name, "repo", repo.Name, "err", err.Error()) continue } open, labelPresent := issueState(issues, t.Spec.Source.Number, approvalLabel) if !open || labelPresent { continue // closed (webhook path), or still awaiting approval } // Approved on SCM but condition not set: flip it (RetryOnConflict). if err := r.flipApprovalApproved(ctx, &t); err != nil { l.Error(err, "approvalBackstop: flip approval", "resource_id", t.Name) continue } r.Metrics.ApprovalBackstopFlip() l.Info("approvalBackstop: recovered missed approval", "action", "approval_backstop", "resource_id", t.Name, "issue", t.Spec.Source.IssueRef) } } // implRunningForIssue reports whether some active (non-terminal, non-AwaitingApproval) // Task other than self references issueRef. func implRunningForIssue(existing []tatarav1alpha1.Task, issueRef, self string) bool { for i := range existing { t := existing[i] if t.Name == self || t.Spec.Source == nil || t.Spec.Source.IssueRef != issueRef { continue } if !isTerminal(t.Status.Phase) && t.Status.Phase != "AwaitingApproval" { return true } } return false } // issueState returns (open, hasApprovalLabel) for number among the open issues // (open=false if number is not in the open list). func issueState(issues []scm.IssueRef, number int, approvalLabel string) (bool, bool) { for _, iss := range issues { if iss.Number == number { return true, hasLabel(iss.Labels, approvalLabel) } } return false, false } func repoByName(repos []tatarav1alpha1.Repository, name string) *tatarav1alpha1.Repository { for i := range repos { if repos[i].Name == name { return &repos[i] } } return nil } func (r *ProjectReconciler) flipApprovalApproved(ctx context.Context, task *tatarav1alpha1.Task) error { return retry.RetryOnConflict(retry.DefaultRetry, func() error { fresh := &tatarav1alpha1.Task{} if err := r.Get(ctx, client.ObjectKeyFromObject(task), fresh); err != nil { return err } apimeta.SetStatusCondition(&fresh.Status.Conditions, metav1.Condition{ Type: tatarav1alpha1.ConditionApprovalApproved, Status: metav1.ConditionTrue, Reason: "ApprovalBackstop", Message: "approval label removed on SCM; recovered by backstop", ObservedGeneration: fresh.Generation, }) return r.Status().Update(ctx, fresh) }) }issueStateonly sees OPEN issues (ListOpenIssues), so a closed issue returns open=false -> skipped (webhook handles close). That is intentional per spec. - Step 4: call
r.approvalBackstop(ctx, proj, reader, repos, existing)once per scan reconcile inrunScans(after the issueScan block, unconditional - it is cheap and self-gating).go test ./internal/controller/ -run ApprovalBackstop-> PASS;go build ./.... - Step 5: commit
feat(scan): approval backstop for missed approval webhooks.
Task 5: Full verification + merge¶
- Step 1:
gofmt -l internal/ api/-> empty. - Step 2:
golangci-lint run ./...-> 0 issues. - Step 3:
make test-> all packagesok. - Step 4: request code review (opus); fix critical/high.
- Step 5: merge worktree branch to operator
main(--no-ff), cleanup worktree.
Task 6: Deploy + config (gated; build from main)¶
- Step 1: push operator
main; build image locally (CI broken)harbor.szymonrichert.pl/containers/tatara-operator:<sha>(amd64, VERSION/COMMIT=, DATE=now); --push. - Step 2: Project CR config (infra helmfile worktree off origin/main): in
helmfiles/tatara/values/tatara-operator/raw/project-tatara.*.yamlsetspec.scm.cron.brainstorm.schedule: "0 * * * *"andspec.scm.cron.brainstorm.maxOpenProposals: 3; invalues/tatara-operator/common.yamlbumpimage.tagto<sha>. CRD changed (new field) ->kubectl applythe regenerated CRD (Helm does not upgrade crds/). - Step 3: infra MR (auto-merge on pipeline); the child pipeline
helmfile applyrolls the operator + re-applies the Project CR. - Step 4: verify live: a repo under cap gets one brainstorm/hour; a repo at 3 open
awaiting-approvalissues is skipped; remove an approval label without a webhook (or simulate) and confirm the backstop flips the Task within the hour.
Self-review notes¶
- Spec coverage: A (Task 3), B stuck-task (Task 4), B orphan (existing issueScan, no task), config + cadence (Task 6), metrics (Task 2), CRD field (Task 1), approvalLabel-empty fallback (Task 3 proposalBacklog). Covered.
- Confirm the activity-label key: grep
scanTaskLabelsfor the exact constant (e.g.labelActivity) and use it inbrainstormInFlightinstead of the literal. - Confirm there is no existing
repoSlug/slug helper before adding one (grepOwnerRepo(in projectscan.go). apimeta,metav1,client,retry,scmalready imported in the controller package.