diff --git a/deploy/postgres/goose/00046_2026-09-16-retire-demo-ten-projects.sql b/deploy/postgres/goose/00046_2026-09-16-retire-demo-ten-projects.sql index b1914804..0814137b 100644 --- a/deploy/postgres/goose/00046_2026-09-16-retire-demo-ten-projects.sql +++ b/deploy/postgres/goose/00046_2026-09-16-retire-demo-ten-projects.sql @@ -1,6 +1,7 @@ -- +goose Up --- Retire goose-seeded demo projects (6001–6010) so they no longer appear in default lists. --- Data is retained for audit; status flips to demo_retired (reversible with UPDATE). +-- Retire goose-seeded demo projects (6001–6010) and their chat sessions +-- so they no longer appear in default lists. Data retained for audit; +-- status flips to demo_retired (reversible with Down). BEGIN; UPDATE projects @@ -9,11 +10,23 @@ SET status = 'demo_retired', WHERE id BETWEEN 6001 AND 6010 AND status <> 'demo_retired'; +UPDATE chat_sessions +SET status = 'demo_retired', + updated_at = now() +WHERE project_id BETWEEN 6001 AND 6010 + AND status <> 'demo_retired'; + COMMIT; -- +goose Down BEGIN; +UPDATE chat_sessions +SET status = 'active', + updated_at = now() +WHERE project_id BETWEEN 6001 AND 6010 + AND status = 'demo_retired'; + UPDATE projects SET status = 'active', updated_at = now() diff --git a/deploy/postgres/migrations/2026-09-16-retire-demo-ten-projects.sql b/deploy/postgres/migrations/2026-09-16-retire-demo-ten-projects.sql index b1914804..0814137b 100644 --- a/deploy/postgres/migrations/2026-09-16-retire-demo-ten-projects.sql +++ b/deploy/postgres/migrations/2026-09-16-retire-demo-ten-projects.sql @@ -1,6 +1,7 @@ -- +goose Up --- Retire goose-seeded demo projects (6001–6010) so they no longer appear in default lists. --- Data is retained for audit; status flips to demo_retired (reversible with UPDATE). +-- Retire goose-seeded demo projects (6001–6010) and their chat sessions +-- so they no longer appear in default lists. Data retained for audit; +-- status flips to demo_retired (reversible with Down). BEGIN; UPDATE projects @@ -9,11 +10,23 @@ SET status = 'demo_retired', WHERE id BETWEEN 6001 AND 6010 AND status <> 'demo_retired'; +UPDATE chat_sessions +SET status = 'demo_retired', + updated_at = now() +WHERE project_id BETWEEN 6001 AND 6010 + AND status <> 'demo_retired'; + COMMIT; -- +goose Down BEGIN; +UPDATE chat_sessions +SET status = 'active', + updated_at = now() +WHERE project_id BETWEEN 6001 AND 6010 + AND status = 'demo_retired'; + UPDATE projects SET status = 'active', updated_at = now() diff --git a/internal/ctxmgr/chat_history.go b/internal/ctxmgr/chat_history.go index bc4c17d4..253ed6ef 100644 --- a/internal/ctxmgr/chat_history.go +++ b/internal/ctxmgr/chat_history.go @@ -117,6 +117,8 @@ func (m *ContextManager) ListChatSessions(ctx context.Context, filter SessionFil LEFT JOIN projects p ON s.project_id = p.id LEFT JOIN chat_messages m ON s.session_id = m.session_id WHERE ($1 = 0 OR s.user_id = $1) AND ($2 = 0 OR s.project_id = $2) + AND COALESCE(s.status, 'active') <> 'demo_retired' + AND COALESCE(p.status, 'active') <> 'demo_retired' GROUP BY s.session_id, s.title, s.user_id, s.project_id, p.name, s.updated_at ORDER BY s.updated_at DESC LIMIT $3 OFFSET $4 @@ -148,6 +150,13 @@ func (m *ContextManager) ListChatSessions(ctx context.Context, filter SessionFil if filter.ProjectID > 0 && sess.ProjectID != filter.ProjectID { continue } + status := sess.Status + if status == "" { + status = "active" + } + if status == "demo_retired" { + continue + } all = append(all, *sess) } diff --git a/internal/ctxmgr/list_sessions_filter_test.go b/internal/ctxmgr/list_sessions_filter_test.go new file mode 100644 index 00000000..5f566f7a --- /dev/null +++ b/internal/ctxmgr/list_sessions_filter_test.go @@ -0,0 +1,45 @@ +package ctxmgr + +import ( + "context" + "testing" + "time" +) + +func TestListChatSessions_SkipsDemoRetired(t *testing.T) { + mgr := NewContextManager(nil, nil, DefaultTimeDecayConfig()) + ctx := context.Background() + userID := int64(1) + + active := &ChatSessionSummary{ + SessionID: "sess-real-u1", + Title: "real", + UserID: userID, + ProjectID: 56, + Status: "active", + UpdatedAt: time.Now(), + } + retired := &ChatSessionSummary{ + SessionID: "sess-wangjing-u1", + Title: "demo", + UserID: userID, + ProjectID: 6001, + Status: "demo_retired", + UpdatedAt: time.Now(), + } + mgr.mu.Lock() + mgr.sessions[active.SessionID] = active + mgr.sessions[retired.SessionID] = retired + mgr.mu.Unlock() + + sessions, err := mgr.ListChatSessions(ctx, SessionFilter{UserID: userID, Limit: 10}) + if err != nil { + t.Fatalf("ListChatSessions: %v", err) + } + if len(sessions) != 1 { + t.Fatalf("expected 1 session, got %d (%+v)", len(sessions), sessions) + } + if sessions[0].SessionID != active.SessionID { + t.Fatalf("expected %s, got %s", active.SessionID, sessions[0].SessionID) + } +} diff --git a/internal/ctxmgr/types.go b/internal/ctxmgr/types.go index 8216cd2f..3b3496a6 100644 --- a/internal/ctxmgr/types.go +++ b/internal/ctxmgr/types.go @@ -208,6 +208,7 @@ type ChatSessionSummary struct { UserID int64 `json:"user_id"` ProjectID int64 `json:"project_id,omitempty"` ProjectName string `json:"project_name,omitempty"` + Status string `json:"status,omitempty"` // active | demo_retired | … MessageCount int `json:"message_count"` LastMessage string `json:"last_message,omitempty"` UpdatedAt time.Time `json:"updated_at"`