feat(memory): 记忆 P2(项目索引物化 / decay 回写 / 作用域写规则 / 静默吞点 + loop 级只读红线测试)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 11:47:39 +08:00
1 parent 9b903a89bc
commit 509dc412eb
12 files changed
+660 -55

No files matched your search

@@ -55,6 +55,31 @@ func (s *Store) PutProject(ctx context.Context, p Project) error {
return nil
}
// EnsureProject 在 id 尚无项目条目时插入一条最小索引记录,已存在时
// **原样保留**(绝不覆盖既有 title/desc)。title 为空时取 id。
//
// 它是「项目记忆链接项目索引」(design §3)的落地路径:项目作用域记忆
// 携带 project_id 指向项目索引层级,只有索引条目存在时该链接才可解析
// (审计 2026-10-05 §4:project 域记忆的 project_id 大量悬空、表为空)。
// 与 PutProject 的区别是**不覆盖**:写入来自记忆时只有 id 可信,不能拿它
// 去改写由 RAG 导入建立的真实 title/desc。空 id 返回 ErrMissingProject。
func (s *Store) EnsureProject(ctx context.Context, id, title string) error {
if strings.TrimSpace(id) == "" {
return ErrMissingProject
}
if strings.TrimSpace(title) == "" {
title = id
}
now := formatTime(time.Now().UTC())
_, err := s.db.ExecContext(ctx, `INSERT INTO project (id, title, desc, created_at, updated_at)
VALUES (?, ?, '', ?, ?)
ON CONFLICT(id) DO NOTHING`, id, title, now, now)
if err != nil {
return fmt.Errorf("projectindex: ensure project: %w", err)
}
return nil
}
// GetProject 返回给定 id 的项目。id 为空或
// 不存在时返回 ErrProjectNotFound。
func (s *Store) GetProject(ctx context.Context, id string) (*Project, error) {