From ed13386612ddcbcee6288717b60c1bba5ca5f57f Mon Sep 17 00:00:00 2001 From: "ki.sagidullin" Date: Mon, 24 Aug 2026 14:56:39 +0500 Subject: [PATCH] =?UTF-8?q?fix(core):=20/retry=20=D1=87=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D1=82=20postmortem-=D1=82=D1=80=D0=B0=D1=81=D1=81=D1=8B?= =?UTF-8?q?=20=E2=80=94=20=D0=BD=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D0=BE=D0=BD=D0=B5=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=BC=D0=BE=D1=80=D1=82=D0=B5=D0=BC=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D0=BA=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/core/core.go | 5 +++++ internal/storage/storage_test.go | 20 ++++++++++++++++++++ internal/storage/traces.go | 11 +++++++++++ 3 files changed, 36 insertions(+) diff --git a/internal/core/core.go b/internal/core/core.go index 4da4306..0e75d03 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -228,6 +228,11 @@ func (c *Core) handleRetry(ctx context.Context, rest string) (Result, error) { if err := c.Store.ClearHistory(ctx, id); err != nil { return Result{}, err } + // Чистый перезапуск: сбрасываем маркер постмортем-анализа от прошлого + // прогона, чтобы на новом failed/timeout постмортем запустился заново. + if err := c.Store.DeleteTracesByAgent(ctx, id, "postmortem"); err != nil { + return Result{}, err + } return Result{ Reply: "Задача перезапущена. Опишите, что меняем:", TaskID: id, diff --git a/internal/storage/storage_test.go b/internal/storage/storage_test.go index ab03614..5ab0936 100644 --- a/internal/storage/storage_test.go +++ b/internal/storage/storage_test.go @@ -264,6 +264,26 @@ func TestUpdateTraceOutput(t *testing.T) { } } +// TestDeleteTracesByAgent — удаление трасс по агенту (сброс маркера при /retry). +func TestDeleteTracesByAgent(t *testing.T) { + s, ctx := setupTestDB(t) + task := &Task{ChatID: "tg://del", TaskTag: "delete-by-agent"} + id, _ := s.CreateTask(ctx, task) + + _, _ = s.AppendTrace(ctx, &Trace{TaskID: id, Agent: "dev"}) + _, _ = s.AppendTrace(ctx, &Trace{TaskID: id, Agent: "postmortem"}) + _, _ = s.AppendTrace(ctx, &Trace{TaskID: id, Agent: "postmortem"}) + + if err := s.DeleteTracesByAgent(ctx, id, "postmortem"); err != nil { + t.Fatalf("DeleteTracesByAgent: %v", err) + } + + traces, _ := s.GetTraces(ctx, id) + if len(traces) != 1 || traces[0].Agent != "dev" { + t.Fatalf("traces = %d (agent %q), want только dev", len(traces), traces[0].Agent) + } +} + // хелперы для проверки классов ошибок func IsNotFound(err error) bool { return errors.Is(err, ErrNotFound) diff --git a/internal/storage/traces.go b/internal/storage/traces.go index 549fe91..8bb776a 100644 --- a/internal/storage/traces.go +++ b/internal/storage/traces.go @@ -107,6 +107,17 @@ func (s *Storage) GetLatestTrace(ctx context.Context, taskID int64, agent string return tr, nil } +// DeleteTracesByAgent удаляет все трассы задачи с заданным агентом +// (сброс маркера при /retry N: чистый перезапуск без наследия анализа). +func (s *Storage) DeleteTracesByAgent(ctx context.Context, taskID int64, agent string) error { + _, err := s.db.ExecContext(ctx, + `DELETE FROM traces WHERE task_id=? AND agent=?`, taskID, agent) + if err != nil { + return fmt.Errorf("%w: delete traces agent %s for task %d: %w", ErrDB, agent, taskID, err) + } + return nil +} + // DeleteTrace удаляет трассу. Только для тестов/админки. func (s *Storage) DeleteTrace(ctx context.Context, id int64) error { res, err := s.db.ExecContext(ctx, `DELETE FROM traces WHERE id = ?`, id)