Revert "feat(opencode): рабочий serve для dev стартует из папки репозитория; пул покрывает внешним сервером подкаталоги"
This reverts commit 2005421739.
This commit is contained in:
@@ -34,12 +34,6 @@ type Pool struct {
|
|||||||
segs map[string]*Server // dir → сервер (root тоже здесь)
|
segs map[string]*Server // dir → сервер (root тоже здесь)
|
||||||
used map[int]bool // занятые порты
|
used map[int]bool // занятые порты
|
||||||
next int // следующий кандидат порта
|
next int // следующий кандидат порта
|
||||||
|
|
||||||
// externs — внешние (уже запущенные) серверы, зарегистрированные как
|
|
||||||
// префикс каталога: Ensure(dir) отдаёт такой сервер и для подкаталогов
|
|
||||||
// зарегистрированного каталога (используется тестами для эмуляции serve,
|
|
||||||
// покрывающего всё дерево worktree).
|
|
||||||
externs map[string]*Server // dir → внешний сервер
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPool создаёт пул. rootDir помечен как служебный (не снимается ReleaseTask).
|
// NewPool создаёт пул. rootDir помечен как служебный (не снимается ReleaseTask).
|
||||||
@@ -98,18 +92,11 @@ func (p *Pool) EnsureRoot(ctx context.Context) error {
|
|||||||
// Возвращает сервер; root-сервер для rootDir возвращается как есть.
|
// Возвращает сервер; root-сервер для rootDir возвращается как есть.
|
||||||
func (p *Pool) Ensure(ctx context.Context, dir string) (*Server, error) {
|
func (p *Pool) Ensure(ctx context.Context, dir string) (*Server, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
|
if s, ok := p.segs[dir]; ok {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
abs := filepath.Clean(dir)
|
abs := filepath.Clean(dir)
|
||||||
if s, ok := p.segs[abs]; ok {
|
|
||||||
p.mu.Unlock()
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
// Внешний сервер-префикс покрывает дерево каталога (тестовая эмуляция):
|
|
||||||
// подкаталог обслуживается тем же сервером, spawn не нужен.
|
|
||||||
if s := p.externalFor(abs); s != nil {
|
|
||||||
p.segs[abs] = s
|
|
||||||
p.mu.Unlock()
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Bin: p.Bin,
|
Bin: p.Bin,
|
||||||
DBPath: p.DBPath,
|
DBPath: p.DBPath,
|
||||||
@@ -142,31 +129,9 @@ func (p *Pool) RegisterExternal(dir, url string) {
|
|||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
abs := filepath.Clean(dir)
|
abs := filepath.Clean(dir)
|
||||||
s := &Server{URL: url, Host: p.Host, PollInterval: 0}
|
p.segs[abs] = &Server{URL: url, Host: p.Host, PollInterval: 0}
|
||||||
p.segs[abs] = s
|
|
||||||
if p.externs == nil {
|
|
||||||
p.externs = map[string]*Server{}
|
|
||||||
}
|
|
||||||
p.externs[abs] = s
|
|
||||||
if p.rootDir != "" && abs == p.rootDir {
|
if p.rootDir != "" && abs == p.rootDir {
|
||||||
p.root = s
|
p.root = p.segs[abs]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// externalFor возвращает внешний сервер-префикс для dir: самого каталога или
|
|
||||||
// его родителя (подкаталоги зарегистрированного каталога обслуживаются тем же
|
|
||||||
// внешним сервером). Вызывается под блокировкой p.mu.
|
|
||||||
func (p *Pool) externalFor(dir string) *Server {
|
|
||||||
cur := dir
|
|
||||||
for {
|
|
||||||
if s, ok := p.externs[cur]; ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
parent := filepath.Dir(cur)
|
|
||||||
if parent == cur {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cur = parent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,25 +142,13 @@ func (w *Worker) hasPostMortemTrace(ctx context.Context, taskID int64) bool {
|
|||||||
//
|
//
|
||||||
// Статус задачи НЕ меняется (failed/timeout остаётся достигнутым); собственные
|
// Статус задачи НЕ меняется (failed/timeout остаётся достигнутым); собственные
|
||||||
// сбои постмортема не влияют на исход задачи — только логируются.
|
// сбои постмортема не влияют на исход задачи — только логируются.
|
||||||
// postMortem запускает постмортем-агент для задачи, завершившейся failed/timeout:
|
func (w *Worker) postMortem(ctx context.Context, task *storage.Task) {
|
||||||
// собирает сессии dev/reviewer из трасс, даёт агенту анализ, сохраняет результат
|
|
||||||
// как trace agent=postmortem и уведомляет владельца задачи резюме.
|
|
||||||
//
|
|
||||||
// projectDir — каталог основного репозитория (тот же serve, где жили сессии
|
|
||||||
// dev/reviewer); fallback на w.Worktree, если папка репо недоступна.
|
|
||||||
//
|
|
||||||
// Статус задачи НЕ меняется (failed/timeout остаётся достигнутым); собственные
|
|
||||||
// сбои постмортема не влияют на исход задачи — только логируются.
|
|
||||||
func (w *Worker) postMortem(ctx context.Context, task *storage.Task, projectDir string) {
|
|
||||||
if w.Store == nil || w.Runner == nil {
|
if w.Store == nil || w.Runner == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.hasPostMortemTrace(ctx, task.ID) {
|
if w.hasPostMortemTrace(ctx, task.ID) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if projectDir == "" {
|
|
||||||
projectDir = w.Worktree
|
|
||||||
}
|
|
||||||
|
|
||||||
traces, err := w.Store.GetTraces(ctx, task.ID)
|
traces, err := w.Store.GetTraces(ctx, task.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -184,7 +172,7 @@ func (w *Worker) postMortem(ctx context.Context, task *storage.Task, projectDir
|
|||||||
if tr.SessionID == "" {
|
if tr.SessionID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tx, tErr := w.Runner.SessionMessages(w.runCtx(ctx, task.ID), projectDir, tr.SessionID)
|
tx, tErr := w.Runner.SessionMessages(w.runCtx(ctx, task.ID), w.Worktree, tr.SessionID)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
log.Printf("worker: task %d: постмортем: транскрипт %s (%s): %v", task.ID, tr.Agent, tr.SessionID, tErr)
|
log.Printf("worker: task %d: постмортем: транскрипт %s (%s): %v", task.ID, tr.Agent, tr.SessionID, tErr)
|
||||||
continue
|
continue
|
||||||
@@ -216,7 +204,7 @@ func (w *Worker) postMortem(ctx context.Context, task *storage.Task, projectDir
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, rErr := w.Runner.Run(w.runCtx(ctx, task.ID), prompt, projectDir, postMortemAgent, "")
|
res, rErr := w.Runner.Run(w.runCtx(ctx, task.ID), prompt, w.Worktree, postMortemAgent, "")
|
||||||
if rErr != nil {
|
if rErr != nil {
|
||||||
log.Printf("worker: task %d: постмортем: запуск: %v", task.ID, rErr)
|
log.Printf("worker: task %d: постмортем: запуск: %v", task.ID, rErr)
|
||||||
w.finalizeTrace(ctx, traceID, storage.TraceFailed, rErr.Error())
|
w.finalizeTrace(ctx, traceID, storage.TraceFailed, rErr.Error())
|
||||||
@@ -246,9 +234,9 @@ func (w *Worker) postMortem(ctx context.Context, task *storage.Task, projectDir
|
|||||||
// postMortemAfter — defer-хук из runTask: запускает постмортем, если задача
|
// postMortemAfter — defer-хук из runTask: запускает постмортем, если задача
|
||||||
// завершилась failed/timeout. Собственные ошибки постмортема не мешают
|
// завершилась failed/timeout. Собственные ошибки постмортема не мешают
|
||||||
// исходному результату задачи (возвращаемый *error только читается).
|
// исходному результату задачи (возвращаемый *error только читается).
|
||||||
func (w *Worker) postMortemAfter(ctx context.Context, task *storage.Task, projectDir string, _ *error) {
|
func (w *Worker) postMortemAfter(ctx context.Context, task *storage.Task, _ *error) {
|
||||||
if task.Status != storage.StatusFailed && task.Status != storage.StatusTimeout {
|
if task.Status != storage.StatusFailed && task.Status != storage.StatusTimeout {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.postMortem(ctx, task, projectDir)
|
w.postMortem(ctx, task)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,11 @@ var devPromptTemplate = template.Must(template.New("dev").Parse(`Ты — dev-а
|
|||||||
{{if .Title}}Название: {{.Title}}{{end}}
|
{{if .Title}}Название: {{.Title}}{{end}}
|
||||||
{{if .Goal}}Цель: {{.Goal}}{{end}}
|
{{if .Goal}}Цель: {{.Goal}}{{end}}
|
||||||
{{if .Repos}}
|
{{if .Repos}}
|
||||||
Репозитории:
|
Репозитории (доступны как подпапки текущего каталога):
|
||||||
{{- range .Repos}}
|
{{- range .Repos}}
|
||||||
- {{.}}
|
- {{.}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Primary}}
|
|
||||||
Основной репозиторий (рабочий каталог этой сессии): {{.Primary}}
|
|
||||||
{{end}}
|
|
||||||
{{if .Why}}Зачем: {{.Why}}{{end}}
|
{{if .Why}}Зачем: {{.Why}}{{end}}
|
||||||
{{if .AC}}Критерии готовности:
|
{{if .AC}}Критерии готовности:
|
||||||
{{.AC}}{{end}}
|
{{.AC}}{{end}}
|
||||||
@@ -33,8 +30,8 @@ var devPromptTemplate = template.Must(template.New("dev").Parse(`Ты — dev-а
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
**Инструкции:**
|
**Инструкции:**
|
||||||
1. Рабочий каталог — сам основной репозиторий ({{.Primary}}): правь файлы в нём прямо, относительными путями (./...).
|
1. Рабочий каталог — общий корень, в котором лежат все репозитории по именам.
|
||||||
{{if .Others}} Связанные репозитории задачи лежат рядом, в ../имя_репо/ (здесь cwd — основной репозиторий): меняй их согласованно, если этого требует задача.{{else}} Работа ведётся только в нём — связанных репозиториев у задачи нет.{{end}}
|
Правь файлы внутри нужного репозитория (./имя_репо/...). Связанные репозитории меняй согласованно.
|
||||||
2. Напиши код, реализующий задачу.
|
2. Напиши код, реализующий задачу.
|
||||||
3. Убедись, что все acceptance criteria выполнены.
|
3. Убедись, что все acceptance criteria выполнены.
|
||||||
4. Пользуйся встроенными инструментами opencode (чтение файлов, поиск, редактирование).
|
4. Пользуйся встроенными инструментами opencode (чтение файлов, поиск, редактирование).
|
||||||
@@ -47,14 +44,9 @@ type DevPromptData struct {
|
|||||||
Title string
|
Title string
|
||||||
Goal string
|
Goal string
|
||||||
Repos []string
|
Repos []string
|
||||||
// Primary — основной (первый) репозиторий задачи: рабочий каталог сессии
|
Why string
|
||||||
// dev — это именно его папка (сервер opencode стартует из неё).
|
AC string
|
||||||
Primary string
|
Steps []storage.Step
|
||||||
// Others — остальные репозитории задачи (доступны рядом как ../имя_репо/).
|
|
||||||
Others []string
|
|
||||||
Why string
|
|
||||||
AC string
|
|
||||||
Steps []storage.Step
|
|
||||||
|
|
||||||
// ReviewFeedback — замечания ревьюера при повторном прогоне dev
|
// ReviewFeedback — замечания ревьюера при повторном прогоне dev
|
||||||
// (не пусто → dev должен исправить именно это).
|
// (не пусто → dev должен исправить именно это).
|
||||||
|
|||||||
@@ -201,12 +201,7 @@ func (w *Worker) pollAndDispatch(ctx context.Context) error {
|
|||||||
func (w *Worker) runTask(ctx context.Context, task *storage.Task) (err error) {
|
func (w *Worker) runTask(ctx context.Context, task *storage.Task) (err error) {
|
||||||
// 0. постмортем-анализ: если задача завершилась failed/timeout — после
|
// 0. постмортем-анализ: если задача завершилась failed/timeout — после
|
||||||
// выхода из runTask (всех путей) запускаем анализ сессий dev/reviewer.
|
// выхода из runTask (всех путей) запускаем анализ сессий dev/reviewer.
|
||||||
// projectDir (каталог основного репозитория) объявляем заранее — defer
|
defer w.postMortemAfter(ctx, task, &err)
|
||||||
// захватывает переменную, её значение подставится к моменту возврата.
|
|
||||||
var projectDir string
|
|
||||||
defer func() {
|
|
||||||
w.postMortemAfter(ctx, task, projectDir, &err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 1. проверяем статус
|
// 1. проверяем статус
|
||||||
if task.Status != storage.StatusApproved {
|
if task.Status != storage.StatusApproved {
|
||||||
@@ -246,12 +241,8 @@ func (w *Worker) runTask(ctx context.Context, task *storage.Task) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. cwd — папка основного репозитория: сервер opencode для работы стартует
|
// 3. cwd — общий каталог (вариант A: один dev видит все репозитории).
|
||||||
// из самого репозитория (подхватывает .serena, экономит токены на поиск
|
cwd := w.Worktree
|
||||||
// файлов из корня worktree). Служебный root-сервер в worktree остаётся
|
|
||||||
// для аналитика; пул поднимает свой serve на каждый репозиторий работы.
|
|
||||||
cwd := w.repoDirOf(repos[0])
|
|
||||||
projectDir = cwd
|
|
||||||
|
|
||||||
// Цикл dev → review, до maxReviewIterations.
|
// Цикл dev → review, до maxReviewIterations.
|
||||||
var feedback []string
|
var feedback []string
|
||||||
@@ -262,8 +253,6 @@ func (w *Worker) runTask(ctx context.Context, task *storage.Task) (err error) {
|
|||||||
Title: task.Title,
|
Title: task.Title,
|
||||||
Goal: task.Goal,
|
Goal: task.Goal,
|
||||||
Repos: repos,
|
Repos: repos,
|
||||||
Primary: repos[0],
|
|
||||||
Others: repos[1:],
|
|
||||||
Why: task.Why,
|
Why: task.Why,
|
||||||
AC: task.AC,
|
AC: task.AC,
|
||||||
Steps: task.Steps,
|
Steps: task.Steps,
|
||||||
|
|||||||
Reference in New Issue
Block a user