package memory import "time" // 本文件是 D3「入库前审查」(design/23 §15.1 D3)的**调优面**:默认值常量、 // ReviewConfig 的旋钮与它们的零值归一化。它从 preinsert.go 拆出来只为守 // AGENTS §10 的文件行数上限(≤500 行)与「模块按职责拆源文件」那条规范 —— // 口径逐字未变。 // // 边界:本文件只管「旋钮长什么样 + 零值取什么默认值」,不认识服务本体 // (PreInsertReviewer 的队列/工作协程在 preinsert.go),也不认识任何开关的 // 运维来源(env 与 `$DATA_DIR/config/memory-tidy.json` 都在组合根)。 // 默认值与审计口径。运算路径上不出现裸魔法数字(AGENTS §10)。 const ( // defaultReviewQueueSize 是有界审查队列长度:审查是「尽力而为」的旁路, // 队列满时宁可少审一条(记忆早已落库),也绝不阻塞写入方。 defaultReviewQueueSize = 256 // defaultReviewScanLimit 是每次审查在同一分区内最多比对的既有条目数。 defaultReviewScanLimit = 200 // defaultReviewTimeout 是单条审查的墙钟上限。 defaultReviewTimeout = 20 * time.Second // reviewAuditWho / reviewAuditVerb 是审查留痕的审计口径:它是**系统任务**, // 不是任何角色(design §4.1 C 的同一理由)。 reviewAuditWho = "system/memoryreview" reviewAuditVerb = "memory.review.ingest" ) // ReviewConfig 携带入库前审查的调优旋钮。零值经 normalize 选取安全默认值。 type ReviewConfig struct { // Enabled 是总开关。关闭时组合根不建、不接、不跑 —— 写入路径行为逐字 // 回到本次改动之前。 Enabled bool // QueueSize 是有界队列长度(≤0 用默认)。 QueueSize int // ScanLimit 是单次审查的候选上限(≤0 用默认)。 ScanLimit int // Timeout 是单条审查的墙钟上限(≤0 用默认)。 Timeout time.Duration // Threshold 是候选召回的余弦门,**复用记忆整理的既有阈值** // (config.MemoryTidy.RecallThreshold,design §13 开放点 2 = 0.80): // 入库前审查与夜间整理必须用同一把尺子,否则同一对条目会有两个结论。 // 非正用设计稿默认值。 Threshold float64 // DisableIngestGate 关闭**同步入库闸**(任务书规格 3 的开关)。 // // 它是**反向**字段,且这是刻意的:bool 的零值必须落在「默认行为」上 —— // 同步闸本机制的默认取向(主人 2026-10-07「把记忆整理功能接到昔涟和记忆库 // 中间做审查,由整理功能进行入库」),若写成正向的 EnableIngestGate,任何没 // 显式配过的地方(含测试与组合根构造的一半配置)都会被读成「关掉」,与默认 // 相反。要一键回到「只插不审」(= 本次改动之前的异步行为),写 // `$DATA_DIR/config/memory-tidy.json` 的 `{"ingestGate": false}`(**零新 // env 键**:`.env` 已冻结,design/13 §2.1)。 DisableIngestGate bool // GateBudget 是同步入库闸的**总墙钟预算**(任务书规格 1:写入路径不许变慢)。 // 非正用代码默认值 defaultReviewGateBudget。超预算一律 fail-open(照常插入 + // 留痕),绝不静默丢。 GateBudget time.Duration // GateScanLimit 是同步入库闸的候选扫描上限。它比异步队列更紧(后者 // ScanLimit 默认 200),因为它在**写入路径**上:非正用代码默认值 // defaultReviewGateScanLimit,且最终还会被 cfg.ScanLimit 收紧取小。 GateScanLimit int // DisableT1 关闭 **T1 灰区模型判定**(design/23 §15.1 D3-2:灰区调一次轻量 // 模型,只许回 merge/modify/keep + 理由、禁产新内容)。 // // 它是**反向**字段,理由与 DisableIngestGate 逐字相同:bool 的零值必须落在 // 「默认行为」上,而 T1 的代码默认值是**开**(主人 2026-10-07:要的就是「真的 // 走记忆管理会话」,T0 仍是第一道零成本闸)。若写成正向的 EnableT1,任何没 // 显式配过的地方(含测试与组合根构造的一半配置)都会读成「关掉」,与默认相反。 // 要一键回到纯规则(零模型调用),写 `$DATA_DIR/config/memory-tidy.json` 的 // `{"t1Judge": false}`(**零新 env 键**:`.env` 已冻结,design/13 §2.1)。 DisableT1 bool // T1Timeout 是**单次** T1 模型调用的墙钟上限。非正用代码默认值 // defaultT1Timeout;最终还会被 cfg.Timeout 收紧取小(见 t1Timeout)。 T1Timeout time.Duration } // normalizeReviewConfig 用设计稿默认值填充零值字段。 func normalizeReviewConfig(cfg ReviewConfig) ReviewConfig { if cfg.QueueSize <= 0 { cfg.QueueSize = defaultReviewQueueSize } if cfg.ScanLimit <= 0 { cfg.ScanLimit = defaultReviewScanLimit } if cfg.Timeout <= 0 { cfg.Timeout = defaultReviewTimeout } if cfg.Threshold <= 0 { cfg.Threshold = defaultTidyRecallThreshold } if cfg.GateBudget <= 0 { cfg.GateBudget = defaultReviewGateBudget } if cfg.GateScanLimit <= 0 { cfg.GateScanLimit = defaultReviewGateScanLimit } if cfg.T1Timeout <= 0 { cfg.T1Timeout = defaultT1Timeout } return cfg }