From 4044c0866ff3f92752133cc4e48de918b5d32405 Mon Sep 17 00:00:00 2001 From: Hermes Date: Sun, 16 Aug 2026 22:13:36 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=BA=D0=B0=D1=82=D0=B0=D0=BB=D0=BE=D0=B3?= =?UTF-8?q?=20worktree=20=D0=BF=D1=80=D0=B8=20=D1=81=D1=82=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D0=B5=20app=20(decider=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B4=D0=BE=20=D0=B2?= =?UTF-8?q?=D0=BE=D1=80=D0=BA=D0=B5=D1=80=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/app.go | 9 +++++++++ internal/app/app_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/internal/app/app.go b/internal/app/app.go index 94b1dc3..0b5c72c 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -58,6 +58,15 @@ func New(configPath, version, updateToken string) (*App, error) { // Относительные пути (db, worktree) — рядом с .exe, а не от CWD запуска. cfg.ResolveExePaths() + // Каталог worktree может отсутствовать на свежей машине. Decider (analyst) и + // воркер запускают opencode с cwd=worktree, а git clone не создаёт родительский + // каталог — поэтому создаём его заранее, до любых запусков субагентов. + if cfg.Paths.Worktree != "" { + if err := os.MkdirAll(cfg.Paths.Worktree, 0o755); err != nil { + return nil, fmt.Errorf("app: создать каталог worktree %s: %w", cfg.Paths.Worktree, err) + } + } + // SQLite для задач и трасс ctx := context.Background() diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 2d7a8ba..cf7ba90 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -108,6 +108,43 @@ func TestMain(t *testing.T) { t.Log("main.go exists") } +// TestNew_WorktreeCreated проверяет, что New создаёт каталог worktree (decider и +// воркер запускают opencode с cwd=worktree — каталог должен существовать заранее). +func TestNew_WorktreeCreated(t *testing.T) { + tmp := t.TempDir() + wt := filepath.Join(tmp, "nested", "worktrees") + configPath := filepath.Join(tmp, "config.yaml") + content := strings.Join([]string{ + "telegram:", + " token: \"test:token\"", + " chat_id: \"12345\"", + "paths:", + " db: \"" + filepath.Join(tmp, "test.db") + "\"", + " worktree: \"" + wt + "\"", + "", + }, "\n") + if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil { + t.Fatalf("write config: %v", err) + } + + a, err := New(configPath, "dev", "") + if err != nil { + t.Fatalf("New() err = %v", err) + } + defer a.Store.Close() + st, err := os.Stat(wt) + if err != nil { + t.Fatalf("worktree должен существовать после New: %v", err) + } + if !st.IsDir() { + t.Fatalf("worktree не директория: %v", st.Mode()) + } + if a.Worker.Worktree != wt { + t.Errorf("Worker.Worktree = %q, want %q", a.Worker.Worktree, wt) + } + t.Logf("worktree создан: %s", wt) +} + // TestNew_UpdateWiring проверяет: 1) update.base_url берётся из update-блока // (не git.base_url); 2) приоритет токена: вшитый updateToken > update.token. func TestNew_UpdateWiring(t *testing.T) {