209 lines
5.8 KiB
Go
209 lines
5.8 KiB
Go
package taskloop
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const goPlanValidatorCommand = "go run ./apps/agent/cmd/agent task-loop validate-plan --workspace <workspace> <candidate-plan>"
|
|
|
|
var cutoverStaticOwnershipDocuments = []string{
|
|
"agent-ops/skills/common/plan/SKILL.md",
|
|
"agent-ops/skills/common/code-review/SKILL.md",
|
|
"agent-ops/skills/project/orchestrate-agent-task-loop/SKILL.md",
|
|
"agent-ops/rules/project/domain/testing/rules.md",
|
|
"agent-ops/rules/project/rules.md",
|
|
}
|
|
|
|
var cutoverProductionRoots = []string{
|
|
"agent-ops/skills/common",
|
|
"agent-ops/skills/project",
|
|
"agent-ops/rules",
|
|
}
|
|
|
|
func TestCutoverProductionOwnershipHasNoReferenceCallerOrStaticRouteTable(t *testing.T) {
|
|
root := repoRoot(t)
|
|
if err := validateCutoverProductionOwnership(root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCutoverProductionOwnershipRejectsInjectedPythonCaller(t *testing.T) {
|
|
root := t.TempDir()
|
|
for _, relativeRoot := range cutoverProductionRoots {
|
|
if err := os.MkdirAll(filepath.Join(root, relativeRoot), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, relative := range cutoverStaticOwnershipDocuments {
|
|
contents := ""
|
|
if relative == "agent-ops/skills/common/plan/SKILL.md" || relative == "agent-ops/skills/common/code-review/SKILL.md" {
|
|
contents = goPlanValidatorCommand
|
|
}
|
|
path := filepath.Join(root, relative)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
relative := "agent-ops/skills/common/other/SKILL.md"
|
|
path := filepath.Join(root, relative)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(path, []byte("python3 dispatch.py --validate-plan candidate\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err := validateCutoverProductionOwnership(root)
|
|
if err == nil || !strings.Contains(err.Error(), "matches") || !strings.Contains(err.Error(), ":1") {
|
|
t.Fatalf("validateCutoverProductionOwnership error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCutoverNodeRemainsSharedRuntimeConsumer(t *testing.T) {
|
|
root := repoRoot(t)
|
|
patterns := []*regexp.Regexp{
|
|
regexp.MustCompile(`agenttask\.NewManager\(`),
|
|
regexp.MustCompile(`taskloop\.New\(`),
|
|
regexp.MustCompile(`NewProvider\(`),
|
|
}
|
|
err := filepath.WalkDir(filepath.Join(root, "apps", "node"), func(path string, entry os.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") {
|
|
return nil
|
|
}
|
|
assertNoPatterns(t, path, patterns)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func assertNoPatterns(t *testing.T, path string, patterns []*regexp.Regexp) {
|
|
t.Helper()
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
scanner := bufio.NewScanner(file)
|
|
line := 0
|
|
for scanner.Scan() {
|
|
line++
|
|
for _, pattern := range patterns {
|
|
if pattern.MatchString(scanner.Text()) {
|
|
t.Fatalf("cutover ownership violation %s:%d matches %q", path, line, pattern.String())
|
|
}
|
|
}
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func validateCutoverProductionOwnership(root string) error {
|
|
callerPatterns := []*regexp.Regexp{
|
|
regexp.MustCompile(`dispatch\.py`),
|
|
regexp.MustCompile(`python3 .*orchestrate-agent-task-loop`),
|
|
}
|
|
staticOwnershipPatterns := []*regexp.Regexp{
|
|
regexp.MustCompile(`gpt-5\.6`),
|
|
regexp.MustCompile(`claude-opus`),
|
|
regexp.MustCompile(`Gemini`),
|
|
regexp.MustCompile(`ornith`),
|
|
regexp.MustCompile(`laguna`),
|
|
}
|
|
documents, err := discoverCutoverProductionDocuments(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, relative := range documents {
|
|
path := filepath.Join(root, relative)
|
|
if err := noPatternViolation(path, callerPatterns); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, relative := range cutoverStaticOwnershipDocuments {
|
|
path := filepath.Join(root, relative)
|
|
if err := noPatternViolation(path, staticOwnershipPatterns); err != nil {
|
|
return err
|
|
}
|
|
if relative == "agent-ops/skills/common/plan/SKILL.md" || relative == "agent-ops/skills/common/code-review/SKILL.md" {
|
|
contents, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !strings.Contains(string(contents), goPlanValidatorCommand) {
|
|
return fmt.Errorf("cutover ownership violation %s lacks %q", path, goPlanValidatorCommand)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func discoverCutoverProductionDocuments(root string) ([]string, error) {
|
|
var documents []string
|
|
for _, relativeRoot := range cutoverProductionRoots {
|
|
directory := filepath.Join(root, filepath.FromSlash(relativeRoot))
|
|
err := filepath.WalkDir(directory, func(path string, entry os.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
if entry.IsDir() {
|
|
relative, err := filepath.Rel(root, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
relative = filepath.ToSlash(relative)
|
|
if entry.Name() == "tests" || strings.HasPrefix(entry.Name(), ".") || relative == "agent-ops/skills/project/orchestrate-agent-task-loop/scripts" {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
if !entry.Type().IsRegular() {
|
|
return nil
|
|
}
|
|
relative, err := filepath.Rel(root, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
documents = append(documents, filepath.ToSlash(relative))
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
sort.Strings(documents)
|
|
return documents, nil
|
|
}
|
|
|
|
func noPatternViolation(path string, patterns []*regexp.Regexp) error {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
scanner := bufio.NewScanner(file)
|
|
line := 0
|
|
for scanner.Scan() {
|
|
line++
|
|
for _, pattern := range patterns {
|
|
if pattern.MatchString(scanner.Text()) {
|
|
return fmt.Errorf("cutover ownership violation %s:%d matches %q", path, line, pattern.String())
|
|
}
|
|
}
|
|
}
|
|
return scanner.Err()
|
|
}
|