mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
8a9a01a70a
## What Repair 13 audited Go CLI defects across doctor recovery, gate routing, evidence ingestion, and session handoffs. Doctor preserves recoverable snapshots and reports unresolved findings; gates retain exact changed-file scope and advisory semantics; malformed evidence fails closed; handoffs report observed Git state and chronological recency. ## Why These failures could overwrite recovery data, skip required checks, admit malformed evidence, or restore stale context. Each defect has a regression witness. Existing skill contracts remain unchanged for this bounded workflow evaluation. ## How I tested - Regression witnesses failed before repair and pass after repair. - Combined Go build, vet, race/shuffle tests with coverage, coverage floor, pinned lint, and complexity checks pass. - Generated projections are current; local aggregate reports 10 passed, 0 failed, 1 optional skip. - All 52 selected gates pass. Authoritative Linux/Windows correctness, security, full registry, and installation CI pass. - Fresh author-distinct OpenAI/Codex review verified all 13 repairs and all 33 changed paths on `84029ee533be0a73ac6c882eb9cf7369d52a0055`, including independent critical race regressions; no findings or unchecked acceptance. - Directory reverse moves retain the existing advisory-lock concurrency boundary; this does not claim exhaustive hostile filesystem-race coverage. ## Checklist - [x] Go build and tests pass, including race detection - [x] No secrets or credentials added - [x] Compatibility behavior documented in the changed contract where applicable
113 lines
3.3 KiB
Go
113 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// gitDiscoveryEnv strips GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE
|
|
// from the environment so git discovery resolves the repository from the
|
|
// working directory, never from a leaked parent-process override. A hook-leaked
|
|
// GIT_DIR is not just a read hazard: a "scoped" `git -C <dir> config ...` under
|
|
// it writes the LEAKED repo's shared config (age-gate-scripts-worktree-gitdir-p62wo).
|
|
func gitDiscoveryEnv() []string {
|
|
env := make([]string, 0, len(os.Environ()))
|
|
for _, entry := range os.Environ() {
|
|
switch {
|
|
case strings.HasPrefix(entry, "GIT_DIR="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_WORK_TREE="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_COMMON_DIR="):
|
|
continue
|
|
case strings.HasPrefix(entry, "GIT_INDEX_FILE="):
|
|
continue
|
|
default:
|
|
env = append(env, entry)
|
|
}
|
|
}
|
|
return env
|
|
}
|
|
|
|
// gitChangedFiles includes tracked and untracked work for handoff evidence.
|
|
// A failed observation is distinct from a successfully observed clean tree.
|
|
func gitChangedFiles(cwd string, limit int) ([]string, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1200*time.Millisecond)
|
|
defer cancel()
|
|
command := exec.CommandContext(ctx, "git", "status", "--porcelain=v1", "-z", "--untracked-files=all")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
out, err := command.Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("observe Git status: %w", err)
|
|
}
|
|
files, err := parseGitStatus(string(out))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if limit > 0 && len(files) > limit {
|
|
files = files[:limit]
|
|
}
|
|
return files, nil
|
|
}
|
|
|
|
func parseGitStatus(raw string) ([]string, error) {
|
|
if raw == "" {
|
|
return nil, nil
|
|
}
|
|
if !strings.HasSuffix(raw, "\x00") {
|
|
return nil, fmt.Errorf("unterminated Git status record")
|
|
}
|
|
records := strings.Split(strings.TrimSuffix(raw, "\x00"), "\x00")
|
|
var paths []string
|
|
for i := 0; i < len(records); i++ {
|
|
record := records[i]
|
|
if len(record) < 4 || record[2] != ' ' {
|
|
return nil, fmt.Errorf("invalid Git status record")
|
|
}
|
|
paths = append(paths, record[3:])
|
|
// Porcelain -z emits a rename/copy destination followed by the
|
|
// original path as a separate NUL-delimited field without a status.
|
|
if strings.ContainsAny(record[:2], "RC") {
|
|
i++
|
|
if i == len(records) || records[i] == "" {
|
|
return nil, fmt.Errorf("missing Git rename/copy source")
|
|
}
|
|
paths = append(paths, records[i])
|
|
}
|
|
}
|
|
return paths, nil
|
|
}
|
|
|
|
// resolveRepoRoot is read-only discovery. AgentOps does not mutate Git state.
|
|
func resolveRepoRoot(cwd string) (string, error) {
|
|
command := exec.Command("git", "rev-parse", "--show-toplevel")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
output, err := command.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("resolve git repo root: %w", err)
|
|
}
|
|
return strings.TrimSpace(string(output)), nil
|
|
}
|
|
|
|
// getCurrentBranch is optional read-only metadata for handoff artifacts.
|
|
func getCurrentBranch(cwd string) (string, error) {
|
|
command := exec.Command("git", "branch", "--show-current")
|
|
command.Dir = cwd
|
|
command.Env = gitDiscoveryEnv()
|
|
output, err := command.Output()
|
|
if err != nil {
|
|
return "", fmt.Errorf("read current branch: %w", err)
|
|
}
|
|
branch := strings.TrimSpace(string(output))
|
|
if branch == "" {
|
|
return "", fmt.Errorf("detached HEAD")
|
|
}
|
|
return branch, nil
|
|
}
|