From 09e572f11ea61b3e1b7990b8745bc6c2178547ee Mon Sep 17 00:00:00 2001 From: kami Date: Wed, 26 Aug 2026 22:53:36 +0400 Subject: [PATCH] Carry the Gitea issue body into the task Found by reading .orchestra/launch.md on the first burn-in task. The rendered context said `Acceptance: Not stated.` and carried the issue title as the whole goal, because Gitea.event parsed the issue body and then dropped it from the TaskCreated payload. The body is the task's own statement of what it wants, which every rendered context ranks above continuity and below only a human decision. The store already reads `description` from TaskCreated; only the provider was silent. Every Gitea-sourced task so far has therefore run on its title alone. Classify as an authority bug, not a model-following failure: no agent could have known what it was not told. Co-Authored-By: Claude Opus 5 --- internal/provider/gitea_comments_test.go | 34 ++++++++++++++++++++++++ internal/provider/provider.go | 6 ++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/internal/provider/gitea_comments_test.go b/internal/provider/gitea_comments_test.go index 80c1f4c..05038a6 100644 --- a/internal/provider/gitea_comments_test.go +++ b/internal/provider/gitea_comments_test.go @@ -2,8 +2,10 @@ package provider import ( "context" + "encoding/json" "net/http" "net/http/httptest" + "strings" "testing" "orchestra/internal/domain" @@ -84,3 +86,35 @@ func TestGiteaCommentsRejectsUnparsableCursorAndHTTPError(t *testing.T) { t.Fatal("http failure must be reported") } } + +// The issue body is the task's own statement of what it wants. It must reach +// the store, because every rendered context ranks it above continuity and +// below only a human decision. +func TestGiteaIngestCarriesTheIssueBody(t *testing.T) { + body := `[{"number":1,"title":"Add a --version flag","body":"Print one version line and exit 0. Keep --help working.","state":"open"}]` + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(body)) + })) + defer srv.Close() + out := &sink{} + g := Gitea{BaseURL: srv.URL, Owner: "kami", Repo: "test-e2e", Project: "test-e2e"} + if _, err := g.Poll(context.Background(), out); err != nil { + t.Fatal(err) + } + if len(out.events) != 1 { + t.Fatalf("events = %d", len(out.events)) + } + var p struct { + Title string `json:"title"` + Description string `json:"description"` + } + if err := json.Unmarshal(out.events[0].Payload, &p); err != nil { + t.Fatal(err) + } + if p.Title == "" { + t.Fatal("title missing") + } + if !strings.Contains(p.Description, "Keep --help working") { + t.Fatalf("description = %q", p.Description) + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c93282b..df86a77 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -302,7 +302,11 @@ func (g Gitea) event(issue giteaIssue, source, project string) domain.Event { for _, l := range issue.Labels { caps = append(caps, l.Name) } - p := map[string]any{"source": source, "external_id": strconv.Itoa(issue.Number), "project": project, "title": issue.Title, "capability": caps} + // The body is the task's own statement of what it wants, which is rank-one + // authority in every rendered context. Dropping it here made every + // Gitea-sourced task run on its title alone, with "Acceptance: Not stated." + // Found on the first burn-in task, 2026-08-26. + p := map[string]any{"source": source, "external_id": strconv.Itoa(issue.Number), "project": project, "title": issue.Title, "description": issue.Body, "capability": caps} b, _ := json.Marshal(p) return domain.Event{ID: domain.NewID(), TaskID: domain.NewID(), Type: "TaskCreated", Version: 1, Payload: b, Surface: string(authz.System)} }