Files
Cyrene_v2/internal/runtime/memory/store_temporal.go
T
AskaEth 82e9213804 test(memory): M45 复看 P2 —— 补「temporal 不许被判定读」的源码级断言 + 注释纠正
P2-1(真缺口):r1 复看的变异 C 证明「在异步审查侧拿 item.Temporal 改判」当时
**整个 memory 包全绿** —— 已落库的行在异步侧被改判这条口径没有守卫,而异步侧
(applyVerdict / setIngestRetention)恰恰最容易被人随手接线。

新增 TestTemporalIsNotWiredIntoJudgement:扫生产代码(非 _test.go、非注释行)里
所有含 `.Temporal` 的行,逐行要求命中 temporalFieldSites 白名单(校验 3 条 / 产出 1 条,
**片段级**而不是文件级 —— 按文件放行会让同一文件里新写的接线路过),违规时报
「文件:行号: 表达式片段」;同时校验白名单每一条确实命中过,防止它老化成空头许可证。

变异实测(复看给的写法,插在 preinsert.go 的 applyVerdict 入口):
  --- FAIL: TestTemporalIsNotWiredIntoJudgement
      temporal 被接进了判定(裁决 D:它只许被记录,不许被判定读)…:
          preinsert.go:476: if item.Temporal == TemporalOneOff {
  删掉接线后全绿。

P2-2(文案与 D 矛盾):store_temporal.go 的归属清单原写「判定在 resolveRetention
(唯一入口,第 4 步)」—— D 之后第 4 步是 N1~N7、没有 temporal 步,那句等于给后来者
指了接线入口。改为:temporal 只被记录、不参与判定;要把 temporal 接进判定必须先拿到
模型真实标签分布,并只能通过 temporalFieldSites 白名单(有意的接线决定)进来。

另按复看提醒:temporal.go 头注里那两条统计 SQL 标明「**运维分析用,不属于读侧代码
路径**」,并写明读侧(timegate 与任何读侧 SQL)明令不许对 created_at/expires_at 用
datetime() —— 那是定宽文本比较,抄进查询层会静默判错。

回归:go build ./... + go vet ./... 全绿;memory/db 包测试全绿;memory -race 38.0s 全绿。
2026-10-26 19:29:16 +08:00

47 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package memory
import (
"errors"
"fmt"
)
// 本文件是 `memory_items.temporal`(迁移 0027)在**写入路径**上的那一半:哨兵错误、
// 字段校验、以及落库时的列值编码。
//
// 🔴 其余三处的归属(别在这条链上另开一份实现):
// - **字段与语义**在 store.go 的 MemoryItem 上(结构体的字段不能跨文件声明),
// 闭集与归一化在 temporal.go;
// - **判定**仍是 `resolveRetention`(retention.go)里的 N1~N7 —— `temporal`
// **只被记录,不参与判定**(r1 复审裁决 D),因此 `resolveRetention` 里
// **没有、也不许有**「第 4 步看 temporal」这种东西;
// - **DDL** 在 migrations/0027 + store_schema.go + temporal_schema.go。
//
// 要把 `temporal` 接进判定,前置条件写在 temporal.go 头注:先拿到模型真实标签分布
// (那里给了两条只读 SQL),再改 `temporal_test.go` 的 temporalFieldSites 白名单 ——
// 那张表是**有意的接线决定**的唯一入口,不许绕过它。
//
// 拆出本文件只为守 AGENTS §10 的文件行数上限(store.go 已到 500 行)与「模块按职责
// 拆源文件」那条规范 —— 口径逐字未变。
// ErrInvalidTemporal 在条目的 Temporal(时间性元数据位,迁移 0027)不是闭集内的
// 值时返回。空串**不在**此列:它是「尚未分型」这一合法状态。
var ErrInvalidTemporal = errors.New("memory: invalid temporal kind")
// validateTemporal 只接受闭集内的值:时间性位是**枚举元数据**,一个自造的值
// ("sometimes")既判不出档位、又不该静默落库 —— 提取器已经把模型输出归一成
// unknown(temporal.go 的 normalizeTemporal),走到这里还是非法值只能是程序错误,
// 因此直接拒,而不是替它挑一个值。
func (it *MemoryItem) validateTemporal() error {
if !it.Temporal.valid() {
return fmt.Errorf("%w: %q", ErrInvalidTemporal, it.Temporal)
}
return nil
}
// temporalColumn 返回要写进 `temporal` 列的值(与 `ingestGatePlan.retention()` /
// `expiresAt()` 同族:把「条目 → 落库值」这一步收在类型自己身上,插入语句里不留
// 裸转换,也就不可能出现两处各转一次、口径分叉)。
func (it MemoryItem) temporalColumn() string {
return string(it.Temporal)
}