Keep the launch dump out of the session's git status
The .orchestra directory showed up as untracked work in the very worktree whose own launch context said "uncommitted changes: false". It would have polluted the quality gate, the review diff, and the agent's reading of git status. A .gitignore of "*" inside the directory ignores it including itself. The worker's done marker lives there too and had the same problem. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -120,7 +120,15 @@ const LaunchContextFile = ".orchestra/launch.md"
|
|||||||
// evidence is worth having, and is not worth refusing to start work over.
|
// evidence is worth having, and is not worth refusing to start work over.
|
||||||
func WriteLaunchContext(worktree, prompt string) error {
|
func WriteLaunchContext(worktree, prompt string) error {
|
||||||
path := filepath.Join(worktree, LaunchContextFile)
|
path := filepath.Join(worktree, LaunchContextFile)
|
||||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
dir := filepath.Dir(path)
|
||||||
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Orchestra's own scratch directory must not show up as the session's
|
||||||
|
// work. A .gitignore of "*" ignores the directory including itself, so the
|
||||||
|
// tree stays clean for the agent, for the gate, and for review evidence.
|
||||||
|
// The worker's done marker lives here too and had the same problem.
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*\n"), 0o644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(path, []byte(prompt), 0o644)
|
return os.WriteFile(path, []byte(prompt), 0o644)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package herdr
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -98,3 +99,29 @@ func TestTmuxSessionNameKeepsCollisionResistantSuffix(t *testing.T) {
|
|||||||
t.Fatalf("unsafe tmux session names %q %q", a, b)
|
t.Fatalf("unsafe tmux session names %q %q", a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The launch dump is Orchestra's scratch space, not the session's work. A
|
||||||
|
// worktree that starts dirty pollutes the gate, the review diff, and the
|
||||||
|
// agent's own reading of `git status`.
|
||||||
|
func TestWriteLaunchContextLeavesTheWorktreeClean(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
for _, args := range [][]string{{"init"}, {"config", "user.email", "t@t"}, {"config", "user.name", "t"}, {"commit", "--allow-empty", "-m", "init"}} {
|
||||||
|
if out, err := exec.Command("git", append([]string{"-C", dir}, args...)...).CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("git %v: %v: %s", args, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := WriteLaunchContext(dir, "the instruction"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b, err := os.ReadFile(filepath.Join(dir, LaunchContextFile))
|
||||||
|
if err != nil || string(b) != "the instruction" {
|
||||||
|
t.Fatalf("launch context = %q, err %v", b, err)
|
||||||
|
}
|
||||||
|
out, err := exec.Command("git", "-C", dir, "status", "--short").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(string(out)) != "" {
|
||||||
|
t.Fatalf("worktree is dirty after a launch dump:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user