package memory import ( "database/sql" "encoding/json" "fmt" ) // 本文件是 memory_items 的**行映射**(scanItem / scanItems 与它们的公共接口 // rowScanner),从 store.go 拆出来只为守 AGENTS §10 的文件行数上限(≤500 行) // 与「模块按职责拆源文件」那条规范 —— 口径逐字未变。 // // 放在独立的源文件里还有一层好处:读路径的每一处「哪些列被读出来」都集中在此, // 与写路径(store.go 的 Add 与 preinsert_gate.go 的闸)对照时不必在 500 行里翻。 // rowScanner 同时被 *sql.Row 与 *sql.Rows 满足,因此单个 scanItem // 辅助函数即可服务 Get 与 ListByPersona。 type rowScanner interface { Scan(dest ...any) error } // scanItems 把每一行读入 MemoryItem 切片,关闭 rows 并用 // 操作名包裹 scan/遍历失败。成功时总是返回 // 非 nil、可能为空的切片。 func scanItems(rows *sql.Rows, op string) ([]MemoryItem, error) { defer func() { _ = rows.Close() }() items := make([]MemoryItem, 0) for rows.Next() { item, err := scanItem(rows) if err != nil { return nil, fmt.Errorf("memory: scan %s: %w", op, err) } items = append(items, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("memory: iterate %s: %w", op, err) } return items, nil } // scanItem 读取一行 memory_items。tags 以 JSON 数组字符串存储; // 时间戳以定宽 UTC 字符串(timeLayout)存储,因此文本 // 比较即可按时间排序。 // // retention_tier 是**第 14 列**(迁移 0026):它与 `kind` 是两根独立的轴, // 因此这里只做「读出来」,不参与任何推导。空串原样成为 // RetentionTierUnset(= 尚未分型),绝不被折成某一档。 // // sensitive 是**第 15 列**(迁移 0028):SQLite 的 INTEGER 0/1 在这里折回 bool。 // 它被读出来的**唯一**理由是要给受限车道的可见性判据用(SharedVisible)—— // 条目不带这一位,过滤就无从过滤;它不参与任何排名/判定。 func scanItem(sc rowScanner) (MemoryItem, error) { var ( it MemoryItem scope, kind, tier string tagsJSON string sensitive int createdAt, updatedAt string ) if err := sc.Scan(&it.ID, &it.PersonaID, &it.RoleID, &it.ProjectID, &scope, &kind, &it.Content, &tagsJSON, &it.Confidence, &it.SourceConversation, &createdAt, &updatedAt, &it.DecayWeight, &tier, &sensitive); err != nil { return MemoryItem{}, err } it.Scope = Scope(scope) it.Kind = Kind(kind) it.RetentionTier = RetentionTier(tier) it.Sensitive = sensitive != 0 if err := json.Unmarshal([]byte(tagsJSON), &it.Tags); err != nil { return MemoryItem{}, fmt.Errorf("memory: decode tags: %w", err) } if it.Tags == nil { it.Tags = []string{} } var err error if it.CreatedAt, err = parseTime(createdAt); err != nil { return MemoryItem{}, err } if it.UpdatedAt, err = parseTime(updatedAt); err != nil { return MemoryItem{}, err } return it, nil }