主人 2026-10-07:「后端自动创建独立的面板配置文件要做」。 - 启动路径(wireMemoryTidy → applyMemoryTidyTuning 之后)若 $DATA_DIR/config/memory-tidy.json 不存在或全空白 => 写一份「当时生效值」 (十键:既有六键 + 频率四键),此后面板接管 - 已存在(非空)=> 跳过,该分支没有任何写盘语句;损坏 => WARN + 按 .env 跑、 一个字不写盘(不「修好它」) - 幂等(第二次启动必落到跳过分支);原子写(tmp 0600 + rename) - 落盘前完整性闸:缺键 / 非正周期 / 钟点越界 => WARN + 不落盘 - 值来自 TidyTuningSnapshot(先跑两个归一化再翻回正向键)=> 播种那一刻运行 行为零变化,有强断言(归一化后配置逐字相等) - 零新增依赖、零 env 键变更;5 个变异(含「让已存在走写盘」=> 红)
86 lines
4.7 KiB
Go
86 lines
4.7 KiB
Go
package memory
|
||
|
||
import (
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/config/runtimeconfig"
|
||
)
|
||
|
||
// 本文件是「启动播种」的**值来源**:把 `$DATA_DIR/config/memory-tidy.json` 十键的
|
||
// **当前生效值**拍成一份完整快照,交给 runtimeconfig.SeedMemoryTidy 落盘。
|
||
//
|
||
// 它回应主人 2026-10-07 的两条口径:
|
||
//
|
||
// - 「**`.env` 只做兜底用** —— 后端**自动创建**独立的面板配置文件之后就走面板
|
||
// 配置文件,这些键优先从面板配置上取」⇒ 播种的时机与「此后以面板为准」;
|
||
// - 「后端**自动创建**独立的面板配置文件**要做**」⇒ 这份快照就是「自动创建」的
|
||
// 内容,**零新 env 键**(`.env` 已冻结,design/13 §2.1)。
|
||
//
|
||
// 为什么放在 memory 而不是 runtimeconfig:runtimeconfig 只管**文件形状与读写**,
|
||
// 「缺位/坏值怎么回落、默认值是多少」属于 TidyConfig / ReviewConfig 的归一化语义
|
||
// (与 normalizeTidyConfig / normalizeReviewConfig 同一层)。
|
||
//
|
||
// 为什么快照取**归一化之后**的值:主人要的是「播种那一刻行为零变化」——把当时真正
|
||
// 生效的值写成文件,读回来叠上去之后(面板这一档等于生效值本身)结果逐字相同。
|
||
// 若快照取的是「未归一化的中间值」(比如四键还是 0),写下去的就是一份**比现状更空**
|
||
// 的配置,只是侥幸等价;取归一化后的值,等价性才是结构性的(见
|
||
// TestTidyTuningSnapshot_PanelRoundTripKeepsEffectiveConfig)。
|
||
//
|
||
// 为什么只有一处实现:十键的默认值在 memory 侧各只有一处定义
|
||
// (tidy_config.go / preinsert_config.go 的 default* 常量),快照直接复用那两个
|
||
// normalize,**不另写一份默认值** —— 否则播种写下去的就是第二份「差不多」的默认值,
|
||
// 会与运行期悄悄分叉。
|
||
//
|
||
// 十键与它们的生效来源:
|
||
//
|
||
// 键 生效来源(无面板文件时) 快照值
|
||
// autoApply DisableAutoApply 的反向(代码默认开) true
|
||
// autoApplyPerRound defaultTidyAutoApplyPerRound 50
|
||
// ingestGate DisableIngestGate 的反向(默认开) true
|
||
// t1Judge DisableT1 的反向(默认开) true
|
||
// t1PerWindow defaultT1PerWindow 120
|
||
// t1WindowSec defaultT1Window 3600
|
||
// intervalSec TidyConfig.IntervalSec(env 或 86400) 归一化值
|
||
// quietIntervalSec 缺省 = intervalSec 归一化值
|
||
// quietStartHour TidyConfig.QuietStartHour(env 或 23) 归一化值
|
||
// quietEndHour TidyConfig.QuietEndHour(env 或 7) 归一化值
|
||
|
||
// TidyTuningSnapshot 拍下十键的**当前生效值**,返回一份可直接落盘的面板快照。
|
||
//
|
||
// 入参是**已经叠过面板层的**两个配置(组合根的次序:TidyConfigFromConfig →
|
||
// ApplyTidyScheduleTuning → 本函数;审查侧同理),本函数只做最后一步归一化并翻成
|
||
// 文件形状 —— 它不读盘、不读 env、不认识面板文件:
|
||
//
|
||
// - tidy 侧四键(intervalSec / quietIntervalSec / quietStartHour / quietEndHour)
|
||
// 与自动档两键(autoApply / autoApplyPerRound);
|
||
// - review 侧四键(ingestGate / t1Judge / t1PerWindow / t1WindowSec):
|
||
// 这四项在 `.env` 里**没有出处**(`.env` 已冻结),只由面板文件与代码默认决定,
|
||
// 而播种只发生在**面板文件不存在**时 —— 那正是「零值 + normalizeReviewConfig」
|
||
// 这一条路径,与运行期(wireMemoryReview 的三个 helper)逐字一致。
|
||
//
|
||
// 两个开关是**反向**字段(DisableX),快照要把它们翻回正向的 JSON 键:文件里的
|
||
// `"autoApply": true` 才是「没配时到底开不开」的如实陈述。
|
||
func TidyTuningSnapshot(tidy TidyConfig, review ReviewConfig) runtimeconfig.MemoryTidyTuning {
|
||
tidy = normalizeTidyConfig(tidy)
|
||
review = normalizeReviewConfig(review)
|
||
|
||
autoApply := !tidy.DisableAutoApply
|
||
ingestGate := !review.DisableIngestGate
|
||
t1Judge := !review.DisableT1
|
||
intervalSec := tidy.IntervalSec
|
||
quietIntervalSec := tidy.QuietIntervalSec
|
||
quietStartHour := tidy.QuietStartHour
|
||
quietEndHour := tidy.QuietEndHour
|
||
|
||
return runtimeconfig.MemoryTidyTuning{
|
||
AutoApply: &autoApply,
|
||
AutoApplyPerRound: tidy.AutoApplyPerRound,
|
||
IngestGate: &ingestGate,
|
||
T1Judge: &t1Judge,
|
||
T1PerWindow: review.T1PerWindow,
|
||
T1WindowSec: int(review.T1Window.Seconds()),
|
||
IntervalSec: &intervalSec,
|
||
QuietIntervalSec: &quietIntervalSec,
|
||
QuietStartHour: &quietStartHour,
|
||
QuietEndHour: &quietEndHour,
|
||
}
|
||
}
|