From 72e6ce66b4c6591dbf7c64b42808af5e0a4ea378 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 30 Sep 2026 03:20:57 +0000 Subject: [PATCH] Advance the core gitlink and serialize empty lists as arrays Why: backend main pinned core 019617e (unfetchable post-migration) and the store List* functions returned nil slices that encode as JSON null, which crashed the frontend nav with .map on null (the 2026-09-30 white-screen). Evidence: go test ./... across all packages; store regression TestEmptyListsSerializeAsEmptyArrays asserts [] for empty project lists. Impact: all 17 store list sites now initialize empty slices; build-blocker core pin resolved; frontend no longer receives null for empty lists. Refs: documents/records/2026-09-30-history-migration.md --- deps/agent-system-core | 2 +- internal/store/auth.go | 4 ++-- internal/store/config.go | 4 ++-- internal/store/content.go | 10 +++++----- internal/store/empty_json_test.go | 28 ++++++++++++++++++++++++++++ internal/store/messages.go | 8 ++++---- internal/store/migrate.go | 2 +- internal/store/projects.go | 4 ++-- internal/store/search_postgres.go | 2 +- 9 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 internal/store/empty_json_test.go diff --git a/deps/agent-system-core b/deps/agent-system-core index 019617e..4ee1979 160000 --- a/deps/agent-system-core +++ b/deps/agent-system-core @@ -1 +1 @@ -Subproject commit 019617e302357f267463cb576adaa28690cd65df +Subproject commit 4ee1979535cedce8ea0e6bc9044cbc09f8b5fb13 diff --git a/internal/store/auth.go b/internal/store/auth.go index 615d349..ff8df12 100644 --- a/internal/store/auth.go +++ b/internal/store/auth.go @@ -61,7 +61,7 @@ func (s *Store) ListUsers() ([]User, error) { return nil, err } defer rows.Close() - var out []User + out := []User{} for rows.Next() { var u User var ca string @@ -111,7 +111,7 @@ func (s *Store) ListAPIKeys(owner string) ([]APIKeyInfo, error) { return nil, err } defer rows.Close() - var out []APIKeyInfo + out := []APIKeyInfo{} for rows.Next() { var k APIKeyInfo var ca string diff --git a/internal/store/config.go b/internal/store/config.go index 9cd33fd..76a040a 100644 --- a/internal/store/config.go +++ b/internal/store/config.go @@ -23,7 +23,7 @@ func (s *Store) ListProfiles(owner string) ([]core.AgentProfile, error) { return nil, err } defer rows.Close() - var out []core.AgentProfile + out := []core.AgentProfile{} for rows.Next() { var payload string if err := rows.Scan(&payload); err != nil { @@ -132,7 +132,7 @@ func (s *Store) ListPlugins() ([]core.PluginManifest, error) { return nil, err } defer rows.Close() - var out []core.PluginManifest + out := []core.PluginManifest{} for rows.Next() { var payload string if err := rows.Scan(&payload); err != nil { diff --git a/internal/store/content.go b/internal/store/content.go index 4c0b34b..3f8a0e3 100644 --- a/internal/store/content.go +++ b/internal/store/content.go @@ -100,7 +100,7 @@ func (s *Store) ListArtifacts(owner, projectID string) ([]core.Artifact, error) return nil, err } defer rows.Close() - var out []core.Artifact + out := []core.Artifact{} for rows.Next() { a, err := scanArtifact(rows.Scan) if err != nil { @@ -178,7 +178,7 @@ func (s *Store) ActiveHITLCards(owner, conversationID string) ([]core.HITLCard, return nil, err } defer rows.Close() - var out []core.HITLCard + out := []core.HITLCard{} for rows.Next() { var payload string if err := rows.Scan(&payload); err != nil { @@ -287,7 +287,7 @@ func (s *Store) searchKnowledgeSQLite(owner string, q core.RetrievalQuery) ([]co return nil, err } defer rows.Close() - var out []core.RetrievalHit + out := []core.RetrievalHit{} for rows.Next() { h, err := scanKnowledgeHit(rows) if err != nil { @@ -318,7 +318,7 @@ func (s *Store) searchKnowledgeLike(owner string, q core.RetrievalQuery, topK in return nil, err } defer rows.Close() - var out []core.RetrievalHit + out := []core.RetrievalHit{} for rows.Next() { var d core.KnowledgeDoc var own, ca string @@ -380,7 +380,7 @@ func ftsQuery(text string) string { } func splitWords(s string) []string { - var out []string + out := []string{} cur := []rune{} for _, r := range s { if r == ' ' || r == '\t' || r == '\n' { diff --git a/internal/store/empty_json_test.go b/internal/store/empty_json_test.go new file mode 100644 index 0000000..89eb20e --- /dev/null +++ b/internal/store/empty_json_test.go @@ -0,0 +1,28 @@ +package store + +import ( + "encoding/json" + "testing" +) + +// 回归:空列表必须序列化为 [] 而非 null(前端 .map 白屏根因)。 +func TestEmptyListsSerializeAsEmptyArrays(t *testing.T) { + s := openTestStore(t) + cs, err := s.ListConversations("owner_none", "prj_none") + if err != nil { + t.Fatalf("ListConversations: %v", err) + } + if cs == nil { + t.Fatal("ListConversations returned nil slice") + } + if b, _ := json.Marshal(cs); string(b) != "[]" { + t.Fatalf("expected [], got %s", b) + } + ps, _ := s.ListProjects("owner_none") + if ps == nil { + t.Fatal("ListProjects returned nil slice") + } + if b, _ := json.Marshal(ps); string(b) != "[]" { + t.Fatalf("expected [], got %s", b) + } +} diff --git a/internal/store/messages.go b/internal/store/messages.go index cffbf15..70b5a78 100644 --- a/internal/store/messages.go +++ b/internal/store/messages.go @@ -55,7 +55,7 @@ func (s *Store) ListMessages(conversationID string) ([]core.Message, error) { return nil, err } defer rows.Close() - var out []core.Message + out := []core.Message{} for rows.Next() { var m core.Message var parts, ca string @@ -172,7 +172,7 @@ func (s *Store) ListRunsByConversation(conversationID string) ([]core.Run, error return nil, err } defer rows.Close() - var out []core.Run + out := []core.Run{} for rows.Next() { r, err := scanRun(rows.Scan) if err != nil { @@ -190,7 +190,7 @@ func (s *Store) ChildRuns(runID string) ([]core.Run, error) { return nil, err } defer rows.Close() - var out []core.Run + out := []core.Run{} for rows.Next() { r, err := scanRun(rows.Scan) if err != nil { @@ -255,7 +255,7 @@ func (s *Store) EventsAfter(conversationID string, since int64, limit int) ([]co return nil, err } defer rows.Close() - var out []core.StreamEvent + out := []core.StreamEvent{} for rows.Next() { var e core.StreamEvent var typ string diff --git a/internal/store/migrate.go b/internal/store/migrate.go index 84fbe03..da7a334 100644 --- a/internal/store/migrate.go +++ b/internal/store/migrate.go @@ -266,7 +266,7 @@ func (s *Store) MigrationStatus() ([]int, error) { return nil, err } defer rows.Close() - var out []int + out := []int{} for rows.Next() { var v int if err := rows.Scan(&v); err != nil { diff --git a/internal/store/projects.go b/internal/store/projects.go index 8d05636..d93dfb3 100644 --- a/internal/store/projects.go +++ b/internal/store/projects.go @@ -99,7 +99,7 @@ func (s *Store) updateProjectRow(owner, id, query string, args ...any) error { } func scanProjects(rows *sql.Rows) ([]core.Project, error) { - var out []core.Project + out := []core.Project{} for rows.Next() { var p core.Project var settings string @@ -141,7 +141,7 @@ func (s *Store) ListConversations(owner, projectID string) ([]core.Conversation, return nil, err } defer rows.Close() - var out []core.Conversation + out := []core.Conversation{} for rows.Next() { var c core.Conversation var ca, ua string diff --git a/internal/store/search_postgres.go b/internal/store/search_postgres.go index 510104d..37c47d7 100644 --- a/internal/store/search_postgres.go +++ b/internal/store/search_postgres.go @@ -25,7 +25,7 @@ func (s *Store) searchKnowledgePG(owner string, q core.RetrievalQuery) ([]core.R return nil, err } defer rows.Close() - var out []core.RetrievalHit + out := []core.RetrievalHit{} for rows.Next() { var d core.KnowledgeDoc var own, ca string