diff --git a/backend/internal/session_manager/manager.go b/backend/internal/session_manager/manager.go index f785f9968..97d91e5a7 100644 --- a/backend/internal/session_manager/manager.go +++ b/backend/internal/session_manager/manager.go @@ -919,7 +919,8 @@ func (m *Manager) RestoreAll(ctx context.Context) error { continue } var ws ports.WorkspaceInfo - if project.Kind.WithDefault() == domain.ProjectKindWorkspace && len(rows) > 1 { + restoredWorkspaceProject := project.Kind.WithDefault() == domain.ProjectKindWorkspace && len(rows) > 1 + if restoredWorkspaceProject { projectRows, rowErr := m.sessionWorktreeRowsToRepoInfos(ctx, project, rec, rows) if rowErr != nil { m.logger.Error("restore-all: workspace rows failed", "sessionID", rec.ID, "error", rowErr) @@ -994,8 +995,13 @@ func (m *Manager) RestoreAll(ctx context.Context) error { if err := m.markSessionWorktreesActive(ctx, rows); err != nil { m.logger.Warn("restore-all: marking worktrees active failed", "sessionID", rec.ID, "error", err) } - if err := m.store.DeleteSessionWorktrees(ctx, rec.ID); err != nil { - m.logger.Error("restore-all: delete consumed worktree marker failed", "sessionID", rec.ID, "error", err) + + // One-shot: drop the consumed marker so it never outlives one restart + // (#2319). A still-live session re-acquires it at the next quit. + if !restoredWorkspaceProject { + if err := m.store.DeleteSessionWorktrees(ctx, rec.ID); err != nil { + m.logger.Warn("restore-all: delete restore marker failed", "sessionID", rec.ID, "error", err) + } } } return nil diff --git a/backend/internal/session_manager/manager_test.go b/backend/internal/session_manager/manager_test.go index 304219bd8..c3829a93e 100644 --- a/backend/internal/session_manager/manager_test.go +++ b/backend/internal/session_manager/manager_test.go @@ -2457,6 +2457,23 @@ func TestRestoreAll_WorkspaceProjectRestoresAndAppliesEachRepo(t *testing.T) { if rt.created != 1 { t.Fatalf("runtime.Create calls = %d, want 1", rt.created) } + rows, err := st.ListSessionWorktrees(ctx, "mer-1") + if err != nil { + t.Fatal(err) + } + if len(rows) != 2 { + t.Fatalf("workspace project rows after RestoreAll = %v, want root and child inventory", rows) + } + states := map[string]string{} + for _, row := range rows { + states[row.RepoName] = row.State + if row.PreservedRef != "" { + t.Fatalf("row %s preserved_ref = %q, want consumed", row.RepoName, row.PreservedRef) + } + } + if states[domain.RootWorkspaceRepoName] != "active" || states["api"] != "active" { + t.Fatalf("workspace project row states = %v, want active inventory", states) + } } func TestReconcileLive_DeadSessionStashedAndTerminated(t *testing.T) {