Reconcile docs with reality; fix module graph, token compare, health #1
@@ -4,6 +4,7 @@
|
||||
package orchestrator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -88,10 +89,18 @@ func (w GitWorktrees) Create(ctx context.Context, t domain.Task) (string, error)
|
||||
// clean, and so its hash survives independent of any later scratch commits.
|
||||
func writeTaskFile(ctx context.Context, worktree string, t domain.Task) error {
|
||||
path := filepath.Join(worktree, "TASK.md")
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
want := continuity.RenderTaskFile(t)
|
||||
// Presence is not identity. Delivery merges the task branch, and the
|
||||
// branch carries this commit, so a later task's worktree branches from a
|
||||
// master that already holds the *previous* task's TASK.md. Returning early
|
||||
// on os.Stat left that file in place, and every immutability check then
|
||||
// failed with "TASK.md changed" against a hash for a task nobody was
|
||||
// running. Found live: run 7 died on retry_limit without ever leaving
|
||||
// research.
|
||||
if got, err := os.ReadFile(path); err == nil && bytes.Equal(got, want) {
|
||||
return nil
|
||||
}
|
||||
if err := os.WriteFile(path, continuity.RenderTaskFile(t), 0644); err != nil {
|
||||
if err := os.WriteFile(path, want, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, args := range [][]string{{"add", "TASK.md"}, {"commit", "-m", "orchestra: TASK.md"}} {
|
||||
|
||||
@@ -125,3 +125,50 @@ func TestGitWorktreesCommitsTaskFile(t *testing.T) {
|
||||
t.Fatalf("TASK.md changed on recreate")
|
||||
}
|
||||
}
|
||||
|
||||
// A delivered task branch carries its own TASK.md, so master ends up holding
|
||||
// the previous task's file. The next worktree must not inherit it: run 7 died
|
||||
// on retry_limit because every release failed "TASK.md changed" against a hash
|
||||
// for a task nobody was running.
|
||||
func TestWorktreeReplacesAnInheritedTaskFile(t *testing.T) {
|
||||
base := t.TempDir()
|
||||
repo := filepath.Join(base, "repo")
|
||||
initRepo(t, repo)
|
||||
|
||||
previous := domain.Task{ID: "previous-task", Project: "p", Title: "the merged one"}
|
||||
if err := os.WriteFile(filepath.Join(repo, "TASK.md"), continuity.RenderTaskFile(previous), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, args := range [][]string{{"add", "TASK.md"}, {"commit", "-m", "merged task branch"}} {
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = repo
|
||||
cmd.Env = append(os.Environ(), "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t", "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("git %v: %v: %s", args, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
current := domain.Task{ID: "current-task", Project: "p", Title: "the one being run"}
|
||||
w := orchestrator.GitWorktrees{Root: filepath.Join(base, "wt"), Repo: repo}
|
||||
path, err := w.Create(context.Background(), current)
|
||||
if err != nil {
|
||||
t.Fatalf("create: %v", err)
|
||||
}
|
||||
got, err := os.ReadFile(filepath.Join(path, "TASK.md"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got) != string(continuity.RenderTaskFile(current)) {
|
||||
t.Fatalf("worktree inherited a stale TASK.md:\n%s", got)
|
||||
}
|
||||
// Committed, not left dirty: the immutability check reads git status.
|
||||
cmd := exec.Command("git", "status", "--porcelain")
|
||||
cmd.Dir = path
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("git status: %v: %s", err, out)
|
||||
}
|
||||
if strings.Contains(string(out), "TASK.md") {
|
||||
t.Fatalf("TASK.md left uncommitted: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user