Add orch strict worktree dispatch

This commit is contained in:
2026-03-19 13:52:51 +08:00
parent 07f4a6fdae
commit 1b0cd723d7
9 changed files with 776 additions and 48 deletions
+222
View File
@@ -1,6 +1,7 @@
package orch
import (
"os"
"path/filepath"
"testing"
)
@@ -504,3 +505,224 @@ func TestOrchDispatchRejectsNonReadyTask(t *testing.T) {
}
assertErrorJSON(t, stdout, "invalid_state")
}
func TestOrchDispatchCreatesStrictWorktree(t *testing.T) {
t.Parallel()
dbPath := filepath.Join(t.TempDir(), "coord.db")
repoPath := initGitRepo(t)
runOrchCommand(
t,
"--db", dbPath,
"--json",
"run", "init",
"--run", "run_blog_worktree_001",
"--goal", "Validate strict worktree dispatch",
)
runOrchCommand(
t,
"--db", dbPath,
"--json",
"task", "add",
"--run", "run_blog_worktree_001",
"--task", "T1",
"--title", "Implement backend",
"--default-to", "worker-a",
)
dispatchOut := runOrchCommand(
t,
"--db", dbPath,
"--json",
"dispatch",
"--run", "run_blog_worktree_001",
"--task", "T1",
"--repo-path", repoPath,
"--workspace-root", ".orch/worktrees",
"--strict-worktree",
"--body", "Implement inside isolated worktree.",
)
var dispatchResp map[string]any
mustDecodeJSON(t, dispatchOut, &dispatchResp)
attempt, ok := nestedValue(t, dispatchResp, "data", "attempt").(map[string]any)
if !ok {
t.Fatalf("expected attempt object, got %#v", nestedValue(t, dispatchResp, "data", "attempt"))
}
if got, _ := attempt["base_ref"].(string); got != "HEAD" {
t.Fatalf("expected base_ref HEAD, got %#v", attempt["base_ref"])
}
expectedCommit := gitHeadCommit(t, repoPath)
if got, _ := attempt["base_commit"].(string); got != expectedCommit {
t.Fatalf("expected base_commit %q, got %#v", expectedCommit, attempt["base_commit"])
}
if got, _ := attempt["branch_name"].(string); got != "orch/run-blog-worktree-001/T1/attempt-1" {
t.Fatalf("unexpected branch name %#v", attempt["branch_name"])
}
worktreePath, _ := attempt["worktree_path"].(string)
if worktreePath == "" {
t.Fatalf("expected worktree_path, got %#v", attempt["worktree_path"])
}
if got, _ := attempt["workspace_status"].(string); got != "created" {
t.Fatalf("expected workspace_status created, got %#v", attempt["workspace_status"])
}
if _, err := os.Stat(worktreePath); err != nil {
t.Fatalf("stat worktree path %s: %v", worktreePath, err)
}
if _, err := os.Stat(filepath.Join(worktreePath, "README.md")); err != nil {
t.Fatalf("expected README.md in worktree: %v", err)
}
message, ok := nestedValue(t, dispatchResp, "data", "message").(map[string]any)
if !ok {
t.Fatalf("expected message object, got %#v", nestedValue(t, dispatchResp, "data", "message"))
}
payload, ok := message["payload_json"].(map[string]any)
if !ok {
t.Fatalf("expected payload_json object, got %#v", message["payload_json"])
}
if got, _ := payload["worktree_path"].(string); got != worktreePath {
t.Fatalf("expected payload worktree path %q, got %#v", worktreePath, payload["worktree_path"])
}
threadID := nestedString(t, dispatchResp, "data", "attempt", "thread_id")
runInboxCommand(
t,
"--db", dbPath,
"--json",
"claim",
"--agent", "worker-a",
"--thread", threadID,
)
runInboxCommand(
t,
"--db", dbPath,
"--json",
"update",
"--agent", "worker-a",
"--thread", threadID,
"--status", "in_progress",
"--summary", "Started inside worktree",
)
reconcileOut := runOrchCommand(
t,
"--db", dbPath,
"--json",
"reconcile",
"--run", "run_blog_worktree_001",
)
var reconcileResp map[string]any
mustDecodeJSON(t, reconcileOut, &reconcileResp)
updatedTasks := nestedArray(t, reconcileResp, "data", "updated_tasks")
if len(updatedTasks) != 1 {
t.Fatalf("expected one updated task after worktree reconcile, got %#v", updatedTasks)
}
}
func TestOrchStrictWorktreeRejectsDirtyRepoWithoutBaseRef(t *testing.T) {
t.Parallel()
dbPath := filepath.Join(t.TempDir(), "coord.db")
repoPath := initGitRepo(t)
if err := os.WriteFile(filepath.Join(repoPath, "dirty.txt"), []byte("dirty\n"), 0o644); err != nil {
t.Fatalf("write dirty file: %v", err)
}
runOrchCommand(
t,
"--db", dbPath,
"--json",
"run", "init",
"--run", "run_blog_worktree_002",
"--goal", "Validate dirty repo rejection",
)
runOrchCommand(
t,
"--db", dbPath,
"--json",
"task", "add",
"--run", "run_blog_worktree_002",
"--task", "T1",
"--title", "Implement backend",
"--default-to", "worker-a",
)
stdout, _, exitCode := executeOrchCommand(
"--db", dbPath,
"--json",
"dispatch",
"--run", "run_blog_worktree_002",
"--task", "T1",
"--repo-path", repoPath,
"--workspace-root", ".orch/worktrees",
"--strict-worktree",
)
if exitCode != 30 {
t.Fatalf("expected invalid_state exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
}
assertErrorJSON(t, stdout, "invalid_state")
if _, err := os.Stat(filepath.Join(repoPath, ".orch", "worktrees", "run_blog_worktree_002", "T1", "attempt-1")); !os.IsNotExist(err) {
t.Fatalf("expected no worktree directory on strict failure, got err=%v", err)
}
}
func TestOrchStrictWorktreeAllowsExplicitBaseRefOnDirtyRepo(t *testing.T) {
t.Parallel()
dbPath := filepath.Join(t.TempDir(), "coord.db")
repoPath := initGitRepo(t)
baseCommit := gitHeadCommit(t, repoPath)
if err := os.WriteFile(filepath.Join(repoPath, "dirty.txt"), []byte("dirty\n"), 0o644); err != nil {
t.Fatalf("write dirty file: %v", err)
}
runOrchCommand(
t,
"--db", dbPath,
"--json",
"run", "init",
"--run", "run_blog_worktree_003",
"--goal", "Validate explicit base ref on dirty repo",
)
runOrchCommand(
t,
"--db", dbPath,
"--json",
"task", "add",
"--run", "run_blog_worktree_003",
"--task", "T1",
"--title", "Implement backend",
"--default-to", "worker-a",
)
dispatchOut := runOrchCommand(
t,
"--db", dbPath,
"--json",
"dispatch",
"--run", "run_blog_worktree_003",
"--task", "T1",
"--repo-path", repoPath,
"--workspace-root", ".orch/worktrees",
"--strict-worktree",
"--base-ref", "HEAD",
)
var dispatchResp map[string]any
mustDecodeJSON(t, dispatchOut, &dispatchResp)
if got := nestedString(t, dispatchResp, "data", "attempt", "base_ref"); got != "HEAD" {
t.Fatalf("expected explicit base_ref HEAD, got %q", got)
}
if got := nestedString(t, dispatchResp, "data", "attempt", "base_commit"); got != baseCommit {
t.Fatalf("expected base_commit %q, got %q", baseCommit, got)
}
}