Files
DCTS/docs/database_refactor_design.md
T
fmq 43b82b1ae2 feat(all): 物理正确性五重硬门槛、输入文件结构化与 fort.55 错位修复、conv 诊断 DB 化与阶段归因修复、ORELAX 收敛修复与导入工具下线
物理正确性校验体系(common/conv_check.rs +494 行)
- 新增 5 类硬门槛:能量守恒(.6)、温度结构(.7)、emflux 积分校验(.emflux,含全 NaN 判失败)、假收敛排查(itek 轨迹首末比)、b 因子合理性(.bfac)
- runner 在 TLUSTY 阶段结束后执行全部校验,任一失败判 final_converged=false
- GridConfig 新增 8 个可配阈值,经 scheduler→executor→runner 全链路透传

输入文件配置结构化重构(config.rs +1453 行)
- TlustyInput 拆为 dot5/nst 分层结构,字段名严格映射 tlusty208.f READ 语句;SynspecInput 重构为 9 个 Fort55Line 子结构体
- 移除 ChainStep.metals 字段,元素集改由 dot5.atoms/ions 显式声明(gen_input5/nst_writer 同步重写为三源融合 / 分层覆盖)
- fort.55 修复行结构 bug:补全分子表行(7→9 行),IDSTD 50→0 错位修正(影响全部光谱线强归一化,需重算 SYNSPEC 阶段)

conv 诊断 DB 化与阶段归因修复(server)
- 单点详情 conv 面板从磁盘 conv.json 改读 DB grid_points.summary_json;grid_points 新增 summary_json/last_elapsed_sec 两列(旧库幂等 ALTER)
- record_task_report 阶段归因列加 CASE 守卫 + clear_synspec 对称处理,修复 synspec-only/TLUSTY-only 重跑污染统计
- 新增 summary_merge.rs 点级增量合并,避免重跑覆盖诊断字段

收敛性 ORELAX 修复与 seed_chain 可配(sdB_cno.yaml + node)
- nl 阶段加 orelax=0.5、seed_nc 加 orelax=0.3,阻尼中温区 relc 振荡发散
- seed_chain 块可配,executor 优先采用用户配置而非内置默认链

导入工具下线
- 删除 import_results 客户端工具及 Windows 推送脚本;移除 /admin/import_seed 端点
- 改为服务端临时 migrate_conv 端点(扫 conv.json 增量合并入库,迁移后可删)

文档与分析
- 新增 1305 失败点根因分析、fort.14 全 NaN 物理含义分析两份深度文档
- spectrum_correctness_analysis 两次修订标注已修复项;fetch_results.sh 修 trap RETURN 的 set -u 报错
2026-08-09 12:09:48 +08:00

34 KiB
Raw Blame History

DCTS 数据库结构修复设计

本文档记录数据库结构的系统性修复方案(2026-08-05 立项)。 依据《docs/database.md》现状与调度/上报链路的实际数据流,覆盖迁移基础设施、 tasks 阶段信息补全、索引、查询重写、死列清理与凭据留存决策、点级阶段化与命名语义修正。

关联文档:database.mdtask_engine_decoupling_design.mddynamic_cpu_slots_design.md


实施状态(2026-08-05 记录)

Phase 状态 关键改动
0 迁移基础设施 已实施 migrations.rs + PRAGMA user_version 运行器,detect 守卫幂等
1 tasks 阶段信息 已实施 M1 failed_stage/summary_json,结算落库 + 前端徽标
2 tasks 索引 已实施 M2 idx_tasks_wf_status_created
3 窗口函数化 已实施 ROW_NUMBER() 重写逐点列表 + §5.3 CTE 排序(删 Rust sort_by
4 死列清理 已实施 M4 DROP COLUMN revokedregistration_secret 留存 nodes
6 删除 task_type 已实施 M6 DROP COLUMN task_type;执行链改 strategies[0] 推导
5a synspec 归因 已实施 M7(版本号按部署序 >6synspec_success_method + 统计桶(含 WorkflowListStats
5b 阶段状态列 已实施 M8 tlusty_status/synspec_status,结算/claim/running 同步,半失败守卫保留 tlusty 终态
P9 归因拆分 已实施 M12 新增 tlusty_success_method + 回填、M13 DROP COLUMN success_method;整体归因改派生(前端 tlusty ?? synspec
7a 注释+前端 已实施 converged/max_relc 等注释契约;"已完成"标签;synspec 过滤器
7b 改名 已实施 ChainStep/PhaseConfig/ResumePolicy/StepSummary、result_valid、trigger_tlusty_fallback、旧环境变量清理
7c converged→completed 已实施 M9 状态值迁移 + M10 快照列改名 + GridPointStatus::Completed + 前端/stats 键
itek 全量保真 已实施 StepSummary.itek_history 逐次迭代诊断随 summary_json 落库
文档同步 已实施 database.md + api.md 同步新 schema/API

每 Phase 合入前均通过全仓 cargo test + cargo clippy -- -D warnings

审查发现与修复(2026-08-05subagent 对抗性审查)

严重度 发现 修复
【严重】CRITICAL 7c 的测试断言 perl "converged"(?!:) 误把结算阶段值 then_some("converged") 改成 "completed",而 claim/running 守卫仍查 'converged'半失败重试时 tlusty_status 被覆盖,守卫完全失效(147 测试未捕获,因测试断言了同样的错误值) 结算阶段值回退 'converged';修正测试断言;补生命周期测试(结算→claim→running→重试结算全程验证守卫保留 tlusty_status
【高】HIGH claim/running 守卫同时保留了 synspec 'failed',与设计 §7.3 打开项 #2「仅 synspec 侧流转」不符(重试期间 synspec_status 停在旧 failed claim/running 改:tlusty 保留终态守卫 + synspec 自由流转为 queued/running
【中】MEDIUM mark_grid_point_imported 不设阶段列,导入点与正常点阶段口径不一致 导入置 tlusty_status='converged'synspec 保持 NULL
【中】MEDIUM 半失败分支隐含依赖 tlusty_enabled=true(脆弱的隐式不变量) tlusty_enabled.then_some("converged") 防御性守卫
【轻微】LOW stats 内部 SQL 别名 AS converged 仍用旧命名 改名 AS completed(内部别名,不影响 JSON
【轻微】LOW M9 detect 数据检测在"空表"边界返回已迁移(跳过) 接受为已知局限(FROM 保留 'converged' legacy 别名兜底)

0. 背景与问题清单

对现有 schemacrates/server/src/db.rs init_tables + crates/mq/src/sqlite_queue.rs)与 数据流(调度 → 队列 → 节点执行 → 上报结算)的审计,确认以下结构性问题:

编号 问题 严重度 现状
P1 tasks 表阶段结果不完整:failed_stagesummary_jsonsynspec_rc/error/sec 全量)上报后即丢 结算 UPDATE 只落 7 个标量(db.rs:1943),阶段细节仅存磁盘 conv.json
P2 tasks 表无 workflow_name 单列索引,COUNT(*) WHERE workflow_name=? 全表扫 仅有 idx_tasks_point_wf_time(point_name, workflow_name, completed_at)
P3 逐点列表 N+1 相关子查询(每 grid_point 行执行一次取最新任务) db.rs:2780 / 2818 LEFT JOIN ... (SELECT ... LIMIT 1)
P4 registration_secret(鉴权凭据)放在 nodes(运行时态表),与 token_hash 分属两表 中(已决策保留) 审批前状态,node_credentials.token_hash NOT NULL 约束决定其只能存 nodes;P4 仅清 revoked 死列
P5 node_credentials.revoked 死列,新代码不读写 db.rs:513 注释确认
P6 grid_points.status 单值掩盖两阶段管线:半失败点(大气收敛+光谱失败)点级只有 failed"大气就绪"不可查询 高(立项) 阶段事实仅存于 tasks 历史
P7 迁移机制脆弱:全部列变更在 init_tables 内手写 PRAGMA table_info + ALTER,无版本追踪 已积累 9+ 块幂等 ALTER
P8 task_type 字段冗余:与 tlusty_strategies[0] 恒等,靠派发时同步维护不变量;synspec-only 场景为"假值" 已废弃兼容字段,全量删除(Phase 6)
P9 TLUSTY-first 命名残留:一批标识符保留单阶段语义,两阶段管线实现时存在语义误判风险(subagent 全库审计) 中(立项) converged/stage/seed_*/旧环境变量见 Phase 7success_method 值域混用列已拆分(M12/M13

已确认不做tasks 表清理/保留策略(用户决策 2026-08-05,仅加索引)。 已确认不做grid_points.id AUTOINCREMENT 移除(一切按 (workflow_name, name) 访问,表重建不值当)。

基础约束

  • 双库分工不动:主库(状态/审计)与队列库(热路径抢占)保持分离。
  • 新增列一律可空,旧节点上报不破坏结算。
  • bundled SQLite 3.45+libsqlite3-sys 0.28)支持 DROP COLUMN、窗口函数、JSON1、UPDATE...RETURNING

1. 总体设计原则

  1. 迁移先行:所有结构变更走 Phase 0 建立的版本化迁移,不再新增 init_tables 手写 ALTER 块。
  2. 可独立交付:每 Phase 独立可上线、可回滚、带测试;全量 cargo test 通过才合入。
  3. 兼容优先failed_stage 缺省兜底 "tlusty"(与 api/task.rs:312 回退默认一致);新列全可空。
  4. 观测不塞进热路径:阶段细节落库在结算事务内完成,不为查询便利增加运行时开销。

2. Phase 0 — 版本化迁移基础设施

2.1 目标

消灭 init_tables 内继续堆积手写 ALTER 块(P7),为 P1–P6 提供统一的迁移通道。

2.2 设计

引入 PRAGMA user_version 驱动的迁移运行器。V0 定义为 0PRAGMA user_version 对全新库的默认值),后续编号迁移从 1 开始:

Database::new
  ├─ init_tables()        现有幂等 bootstrap:建全表 + 既有列检测补全 → 置 user_version = V0(=0)
  └─ apply_migrations()   从当前 user_version 顺序应用 M1..Mn → 每个迁移独立事务
  • 新库bootstrap 建表(CREATE TABLE 始终是最新形态,含后续 Phase 新增列)→ 置 V0 → 应用 V0+1..N每个迁移自带 detect 守卫,已存在的列/索引直接跳过,故新库上所有迁移为 no-op。
  • 旧库user_version=0 但表已存在):bootstrap 幂等跑一遍(含既有列检测补全)→ 置 V0 → 应用后续迁移。detect 守卫保证只补缺的列/索引,数据零搬运。
  • 幂等性关键SQLite 无 ADD COLUMN IF NOT EXISTS,迁移必须靠 detect 守卫而非裸 SQL 数组实现幂等(审查 CRITICAL#3 修复——否则全新库上 bootstrap 已建新列,迁移再 ADD 会报 duplicate column 崩启动)。
  • 迁移定义集中到新文件 crates/server/src/migrations.rs
pub struct Migration {
    pub version: u32,                          // > V0(=0) 的顺序号(1..N
    pub name: &'static str,                    // 便于日志与审计
    pub detect: fn(&Connection) -> Result<bool>, // 该迁移是否已应用(列/索引存在性)
    pub up: &[&str],                           // 未应用时才执行,同事务内顺序执行
}

pub const MIGRATIONS: &[Migration] = &[ /* M1, M2, M4, M6, M7(=5a) */ ];

> **迁移编号与部署顺序约束(实施修正 2026-08-05**:迁移版本号必须与 §11 部署顺序单调一致——
> 5a 部署在 Phase 6 之后,故其版本号取 **M7**(而非早期草案的 M5)。否则已升到 v6 的库会因
> `version <= current` 跳过 5a`synspec_success_method` 列永不创建。

pub fn current_version(conn: &Connection) -> u32;      // PRAGMA user_version
pub fn apply_migrations(conn: &mut Connection) -> Result<()>;
  • 运行器逻辑:读 current_version → 对每个 version > current 的迁移,先跑 detect
    • detect = true(已应用,如新库 bootstrap 已建列)→ 仅推进 user_version,不执行 up
    • detect = falseBEGIN IMMEDIATE → 执行 up SQL → PRAGMA user_version = VCOMMIT
    • 中途失败不推进版本(进程启动时重试)。
  • 与现有 sqlite_queue.rs 的迁移惯用法(PRAGMA table_info 检测)并存:队列库无用户版本迁移,维持现状;仅主库引入版本号。

2.3 代码改动

文件 改动
crates/server/src/migrations.rs 新增(迁移表 + 运行器)
crates/server/src/db.rs init_tables 末尾置 user_version = V0Database::new 在 bootstrap 后调用 apply_migrations

2.4 测试

  1. 新库全流程:Database::new → 版本 = 最新,全部迁移已应用。
  2. 旧库升级:手工构造"缺若干列/索引"的旧 schema 库 → bootstrap + 迁移 → 断言列/索引/数据完整。
  3. 迁移中断恢复:模拟 M2 中途失败 → user_version 停在 M1 → 重跑后 M2 成功。

3. Phase 1 — tasks 表阶段信息补全(P1)

3.1 目标

让"哪个阶段失败"failed_stage)与"完整阶段结果"summary_json)在 DB 可查,替代"只能翻磁盘 conv.json"。

3.2 数据流核实(关键前提)

节点上报 TaskReportmodels.rs:496-513)已携带全部所需字段:

  • converged = TLUSTY 大气收敛标志reporter.rs:32 注释确认,非整管线);
  • status = 整管线成败(derive_report_status:半失败 = Failedreporter.rs:38-45);
  • failed_stage = "tlusty"/"synspec" 精确归因(infer_failed_stagereporter.rs:15-27);
  • summary_json = Rust 侧 ModelSummary 序列化(含 synspec_rc/synspec_error/synspec_sec 与各子步骤摘要)。

服务端 record_task_reportdb.rs:1920)已持有 &TaskReport——无需改签名,只在结算 UPDATE 补两列。

保真边界(审查修正,2026-08 更新)ModelSummarymodels.rs:616-641)与 StepSummary590-612)现已携带逐次 itek 迭代诊断数组(StepSummary.itek_history: Vec<IterCheck>runner.rs:425 填充),随 summary_json 全量落库(见状态表「itek 全量保真 已实施」),不再仅存磁盘 conv.json。落库的 summary_json 与磁盘 conv.json 同源于 runner 的 StepSummary,含 last_iter / worst_depth / n_depths 与完整收敛轨迹。

3.3 迁移 M1tasks 表,均在线 ADD COLUMN

ALTER TABLE tasks ADD COLUMN failed_stage TEXT;     -- "tlusty" / "synspec"
ALTER TABLE tasks ADD COLUMN summary_json TEXT;      -- 完整 ModelSummary 全保真

3.4 代码改动

位置 改动
db.rs:1943 结算 UPDATE SET 增加 failed_stage = COALESCE(?10, 'tlusty')summary_json = ?11;绑定 report.failed_stage / report.summary_json
db.rs list_point_attempts2848 SELECT 增加 failed_stagesummary_json
common/src/models.rs AttemptRow 增加 failed_stage: Option<String>summary_json: Option<String>#[serde(default)] 保 API 兼容)
dashboard 逐尝试列表 阶段徽标(TLUSTY / SYNSPEC+ synspec 错误摘要(解析 summary_json

错误路径reporter.rs:85 失败上报的 summary_json = {"error": ...} 非 ModelSummary——前端解析必须容错(解析失败即显示错误文本),服务端仅透传不解析。

3.5 测试

  • 全状态往返:completed / failed / timeout / 半失败(大气成+光谱败)/ synspec-only / 旧节点(failed_stage=None → 兜底 "tlusty")。
  • record_task_report 后 tasks 行 failed_stagesummary_json 与上报一致。
  • list_point_attempts 返回新字段。
  • 错误路径:summary_json 为 {"error":...} 时前端容错展示。

4. Phase 2 — tasks 表索引(P2,仅索引,不做清理)

4.1 目标

消除 workflow_name 单列查询的全表扫。

4.2 迁移 M2

CREATE INDEX IF NOT EXISTS idx_tasks_wf_status_created
  ON tasks(workflow_name, status, created_at);

用户决策(2026-08-05:不做 tasks 清理/保留策略。tasks 作为完整审计日志长期保留; 此索引覆盖 COUNT(*) WHERE workflow_name=?find_stale_pending_points 的 JOIN 侧(按 point_name+workflow_name 定位)。 注意:find_stale_pending_points 的过滤条件(status='pending' AND created_at < ...,无 workflow_name 前置)不受此索引覆盖——该查询由 JOIN 侧既有 idx_tasks_point_wf_time 支撑,随日志增长扫描量线性增加,属已接受的权衡。

4.3 测试

  • EXPLAIN QUERY PLAN 验证 SELECT COUNT(*) FROM tasks WHERE workflow_name=? 走索引扫描。
  • 既有 find_stale_pending_points / 工作流统计测试不回归。

5. Phase 3 — 逐点列表窗口函数化(P3)

5.1 目标

消除 get_workflow_points / get_workflow_point_row 的逐行相关子查询(数千点 → 数千次子查询)。

5.2 设计

用单遍 ROW_NUMBER() 窗口取每点最新任务,替代 LEFT JOIN tasks ON task_id = (SELECT ... LIMIT 1)

SELECT * FROM (
  SELECT gp.name, gp.teff, ..., gp.status, gp.tlusty_success_method, gp.synspec_success_method, gp.attempt_count,
         t.max_relc, t.task_type, t.seed_point_name, t.node_id,
         t.completed_at, t.error_message, t.failed_stage, t.summary_json,
         COALESCE(t.elapsed_sec, gp.last_elapsed_sec) AS eff_elapsed,
         ROW_NUMBER() OVER (
             PARTITION BY gp.name, gp.workflow_name
             ORDER BY t.completed_at IS NULL, t.completed_at DESC, t.created_at DESC
         ) AS rn
  FROM grid_points gp
  LEFT JOIN tasks t
    ON t.point_name = gp.name AND t.workflow_name = gp.workflow_name
) WHERE rn = 1 AND {where_sql} ORDER BY ... {limit} {offset}

外层再叠加筛选/排序/分页,保持与 get_workflow_points 现签名((total, points) + 过滤 + 分页)一致。 get_workflow_point_row(单点)同样改写,外层 WHERE workflow_name=? AND name=? AND rn=1

5.3 附带优化(可选)

claim_pending_grid_pointsdb.rs:1246SQL 与 Rust 侧双重排序(db.rs:1282 list.sort_by): UPDATE...RETURNING 不保序,用 CTE 让子查询排序贯穿,删掉 Rust 侧 sort_by

5.4 测试

  • 用既有测试网格样本断言新旧查询结果集逐行等价(含:无任务点 → LEFT JOIN 为 NULL、多点多尝试 → 取最新、completed_at NULL 排序规则)。
  • 边界回归(审查修正):构造"点只有未完成任务(completed_at 全 NULL"的样本,断言 ROW_NUMBER 与相关子查询取到同一行——窗口函数与子查询的 NULL 排序语义须显式验证等价。
  • 分页 total 计数不变。

6. Phase 4 — 死列清理(P5registration_secret 保留在 nodes

6.1 目标

清除死列 node_credentials.revokedP5)。registration_secret 不迁移——审查确认移动它存在两个结构性障碍(见 §6.2),且保留在 nodes 语义自洽。

决策:保持两表分离不合并;registration_secret 保留在 nodes,加注释消除"凭据半吊子"的困惑。

6.2 为什么 registration_secret 不迁移(审查 CRITICAL#1/#2

  1. 数据丢失register_nodedb.rs:589)只 INSERT nodes,不建 node_credentials 行;approve_nodedb.rs:618)才建。pending 节点无 node_credentials 行,UPDATE 迁移会静默跳过它们,DROP COLUMN 后凭据永久丢失且无恢复路径。
  2. schema 约束token_hash TEXT NOT NULLdb.rs:518+ idx_node_credentials_token_hash 唯一索引——为 pending 节点 INSERT 空占位会撞唯一索引;改 nullable 需整表重建。
  3. 语义registration_secret 是审批前一次性凭据(注册时产生、取 token 前消费),node_credentials 是审批后 token 状态——放 nodes 与 status='pending_approval' 同生命周期,自洽。

6.3 迁移 M4(清死列)

ALTER TABLE node_credentials DROP COLUMN revoked;   -- bundled SQLite 3.45+,涉及表重建,低峰窗口

6.4 代码改动

  • 仅注释:nodes.registration_secret 列加注释"审批前一次性凭据,唯一例外;token 哈希见 node_credentials"。
  • register_node / approve_node / check_status / reject_node 全链路零改动

6.5 测试

  • revoked 列消失,CREATE TABLE IF NOT EXISTS 对新库不再创建该列。
  • 注册 → 审批 → 取 token 全链路回归(registration_secret 行为不变)。

7. Phase 5 — grid_points 阶段化(立项)

7.1 目标

解除 P6:点级 status 单值无法表达两阶段管线。半失败点(大气收敛 + 光谱失败)需在点级可识别"大气就绪、光谱待重试";synspec 收敛归因需点级可查。

7.2 阶段语义(基于已核实的上报数据)

record_task_report 结算时可从报告精确推导各阶段状态:

上报条件 整体 status tlusty_status synspec_status
tlusty_enabled=0 NULL(不适用) synspec_rc/synspec_error 定:成功→converged,失败→failed
converged=true + synspec 成功 converged converged converged
converged=true + synspec 失败(半失败) failed converged failed
converged=falsetlusty 启用) failed failed pending(未运行)
  • converged = 大气收敛标志(reporter.rs:32);synspec 成败取自 summary_json.synspec_rc / synspec_error
  • 整体 status 仍是调度/策略/筛选的权威状态,阶段列是补充可查信息,不替代它。

7.3 分步交付

5a(先做,轻量):synspec_success_method 归因列

ALTER TABLE grid_points ADD COLUMN synspec_success_method TEXT;

结算时(settlement 事务内)设置,镜像既有大气归因的 synspec 分支逻辑(db.rs:1973-1984)。 已实施(P9 拆分,M12/M13:原 success_method 值域混用列拆为 tlusty_success_method + synspec_success_method 两个阶段列并删除原列,整体归因改由消费方派生(tlusty ?? synspec)。

CASE WHEN synspec 成功
     THEN json_extract(tasks.synspec_strategies, '$[0]')
END

收益:解锁"光谱以 standard 等策略收敛了多少点"的 SQL 统计。无瞬态状态同步负担。 统计 SQL 改动归本阶段独占db.rs:2325-2574 加 synspec 策略桶),P7a 只做前端过滤器引用,避免两阶段重复改同一处(审查 HIGH#2)。

5b(再议,完整):阶段状态列

ALTER TABLE grid_points ADD COLUMN tlusty_status TEXT;   -- pending/queued/running/converged/failed
ALTER TABLE grid_points ADD COLUMN synspec_status TEXT;  -- pending/running/converged/failed/not_required

同步触点(与 grid_points.status 的既有更新点对齐,均为机械追加列):

  • claim_pending_grid_pointsdb.rs:1246)→ 相关阶段列置 queued
  • mark_grid_point_runningdb.rs:1508)→ 相关阶段列置 running
  • record_task_report 结算(db.rs:1994-2004)→ 按 7.2 语义表置阶段终态;
  • 回退重置(scheduler.rs trigger_strategy_fallback)→ 半失败重试时保持 tlusty_status=converged,仅重置 synspec_status

打开项(5b 实施前需定)

  1. not_required(阶段关闭)与 NULL(未配置)的取值约定;
  2. 半失败重试期间 tlusty_status 是否可被后续 claim 覆盖(建议:不可,仅 synspec 侧流转);
  3. initialize_grid 各策略(skip_converged / skip_failed / force_recompute)如何对待阶段状态(建议:以整体 status 为准,阶段列只读展示)。

建议 5b 在 5a 上线、summary_json 数据积累后评估——届时"阶段状态是否需要独立列,还是 join tasks 已够"有真实数据支撑。


8. Phase 6 — 删除 task_type(全量删除,P8

8.1 目标与前提

消除冗余字段、synspec-only 假值坑、以及"派发时同步维护 task_type == strategies[0] 不变量"的负担。

前提(用户决策 2026-08-05:集群无历史节点server/node 随同一镜像统一重建,不存在旧节点反序列化兼容问题。

8.2 执行链改由 strategies 推导(runner + executor

现状:executor 传 task.task_typecustom_chain 恒为 Noneexecutor.rs:172-175),runner 用 task_type 选执行链(runner.rs:299-301)。

改造:executor 按 tlusty_config.strategies[0] 显式推导执行链并作为 custom_chain 传入:

// executor.rs
let chain = match task.tlusty_config.current_strategy("cold_run") {
    "seed_step" => common::runner::default_seed_chain(),
    _ => common::runner::default_cold_chain(),
};
runner.run_model_with_timeout(..., Some(chain), ...);

runnerrun_model_with_timeouttask_type: TaskType 参数改为 current_strategy: &str unwrap_or_else 兜底改按策略串匹配;TaskType 枚举整体删除。 default_cold_chain() / default_seed_chain()runner.rs:16, 66)已 pub,无需改动。

8.3 models.rs

  • TaskSpec 移除 task_type 字段(models.rs:398);
  • 删除 normalize_compat 的 task_type 回填分支(models.rs:435-444);
  • 删除 TaskType 枚举定义。

8.4 调度器(scheduler.rs

  • 删除 strategy_to_task_type239-244);
  • 全部 TaskSpec { task_type: ... } 构造(465, 776, 836)移除该字段。

8.5 DB 迁移 M6 + 配套查询改造

ALTER TABLE tasks DROP COLUMN task_type;   -- bundled SQLite 3.45+,涉及表重建,低峰窗口
位置 改造
db.rs:1979 success_method 归因 ELSE task_typeELSE json_extract(tlusty_strategies, '$[0]')(不变量成立时等价,从此归因全派生)。后经 P9 拆分(M12/M13)改 tlusty_success_method + synspec_success_method 两阶段列并删原列
db.rs:1777-1780 insert_task 删除 task_type_str 解析与绑定
db.rs:2848 list_point_attempts SELECT 去 task_type
db.rs:1859-1866 has_pending_tasks_for_point Some(tt) 分支 AND task_type = ?3AND json_extract(tlusty_strategies, '$[0]') = ?3(生产仅传 None,此分支测试用)
models.rs AttemptRow / API 响应 task_type 字段(#[serde(default)] 兼容已发前端)

8.6 前端

  • pointsTable.js:222last_task_type
  • pointPanel.js:116task_type 徽标;
  • overview.js:211 注释清理。

8.7 测试

  • 全量修正 TaskType::ColdRun / TaskType::SeedStep 引用(实际计数:scheduler.rs 18 处、db.rs 14 处 + AttemptRow 字段、api_tests.rs 20+ 处 JSON 断言、sqlite_queue.rs 7 处、runner.rs 5 处、executor.rs 2 处——编译器兜住全部,属纯工作量);
  • 新增:executor 按 strategies[0]="seed_step" 走种子链的用例;
  • has_pending_tasks_for_point 过滤改 strategies[0] 后的等价断言。

8.8 升级注意

队列库(dcts_queue.db)可能残留升级前序列化含 task_type 的在途 payload。新代码 serde 默认忽略未知字段可正常反序列化,但seed_step 消息的 strategies 可能被 serde default 填成完整链 [cold_run, seed_step] 导致误判 cold_run——升级前确认队列为空(或停机时清空队列)。

8.9 收益与风险

收益:消除冗余列与假值坑;不再维护同步不变量;归因/过滤全部改派生口径。 风险:runner 执行链行为需回归(seed_step 消息必须走种子链);测试改动面大但机械。


9. Phase 7 — 命名语义修正(TLUSTY-first 残留,P9

9.1 背景

系统最早仅 TLUSTY(SYNSPEC 默认不可配置),演进为两阶段管线后,一批标识符保留"仅 TLUSTY"语义, 实现完整计算过程时存在语义误判风险。本 Phase 由 subagent 全库审计产出(2026-08-05), 分注释契约、前端修正、改名、清理四档。

9.2 最危险的 5 个语义混淆

# 名称 位置 问题 处置
1 converged models.rs:603 / reporter.rs:32 / runner.rs:605-616 TLUSTY 启用时=大气收敛;synspec-only 被重写为管线成功;读方无法凭字段名判断语义 7a 注释契约 + 7b 改名
2 StageConfig vs EngineStageConfig runner.rs / models.rs:330 "stage"同词两层义:TLUSTY 计算链子步骤(lte/nc/nl) vs TLUSTY/SYNSPEC 管线大阶段;易把 SYNSPEC 误加进 stages 7b 改名
3 success_method 值域 db.rs:1973-1984 synspec-only 塞入策略名(standard);统计分桶(db.rs:2325-2574)与前端过滤器仅认 cold_run/seed_stepsynspec 收敛点统计丢失 已解决(M12/M13 拆分 + 5a 加桶):拆 tlusty_success_method/synspec_success_method 两阶段列,synspec-only 不再污染大气列
4 seed_point_name / seed_atmos_path models.rs:399 / executor.rs:70-83 TLUSTY seed_step=热启动种子;synspec-only=光谱输入大气来源点,双义无注释 7a 注释
5 task_type 余光 scheduler.rs:458 synspec-only 固定填 cold_run,前端/CSV 误标"冷启动" Phase 6 已覆盖;7a 处理前端残留

9.3 分步实施

7a(先做,无破坏性)——注释契约 + 前端可感知修正

  • 注释契约(不改名):
    • converged 双义标注(TaskReport.converged = "本次结果是否可用",非"大气收敛");
    • max_relc / final_max_relc 标注"仅 TLUSTY 大气迭代有效"
    • cno_sum / wave 标注"TLUSTY 大气金属丰度排序"
    • elapsed_sec vs synspec_sec 标注包含关系(总耗时含 TLUSTY+SYNSPECsynspec_sec 是子集);
    • seed_point_name 标注 synspec-only 双义。
  • 前端可感知修正bug 级):
    • "已收敛" → "已完成"pointsTable.js:44 等所有中文标签);
    • "方法"过滤器增加 synspec 策略名选项(pointsTable.js:48-51);
    • CSV 列 last_task_typeoverall_methodpointsTable.js:218P9 拆分后改 overallMethod(p) = tlusty ?? synspec 派生值);
    • pointMethodBadge(a.task_type) 改从 strategy 推导(pointPanel.js:116);
    • "阶段链诊断" → "TLUSTY 收敛链诊断"pointPanel.js:152);
    • "方法"过滤器的 synspec 选项数据源 = Phase 5a 落库的 synspec_success_method(统计 SQL 改动归 5a 独占,本阶段不再触碰 db.rs:2325-2574)。

7b(Phase 6 后,纯改名,无行为变化)

  • StageConfigChainStepStageSummaryStepSummaryTLUSTY 子步骤层);
  • EngineStageConfigPhaseConfigStagePolicyResumePolicy(管线大阶段层);
  • ModelSummary.stages 加注释"TLUSTY iteration steps, NOT pipeline phases"
  • TaskReport.convergedresult_valid
  • trigger_seed_step_fallback 删除(或改 trigger_tlusty_fallback);
  • 旧环境变量回退清理:CNO_PORT / CNO_SERVER_URL / DCTS_RESULTS_DIR / DCTS_ARCHIVE_DIRGridConfig.results 死字段。

7c(可选,大迁移;2026-08-05 已实施

  • GridPointStatus::ConvergedCompletedenum 改名 + DB 文本值 'converged''completed'(全链 SQL 字面量 + 数据迁移 M9 + 快照列改名 M10 + 前端/统计键)。From<&str> 保留 'converged'/'done' 为 legacy 别名(M9 前旧数据)。实施细节:M9 UPDATE grid_points SET status='completed' WHERE status='converged' M10 RENAME COLUMN workflow_progress_snapshots.converged → completedstats 顶层键 "converged""completed" cold_run_converged/seed_step_converged/synspec_converged 方法限定键保留)。

9.4 测试

  • 7a:前端过滤/统计对 synspec 收敛点的等价断言;注释类无测试。
  • 7b:改名后全量 cargo test(编译期即验证)+ cargo clippy -- -D warnings
  • 7c:M9/M10 迁移专项测试(状态值迁移、快照列改名)+ 全量回归。

9.5 数据库列名审计结论

DB 列名绝大多数如实反映存储内容,无需改名;TLUSTY-first 残留集中在列值概念层面而非列名。 逐表核实结论如下。

需要改动的列(已由既有 Phase 覆盖)

动作 对应
tasks.task_type 删除 Phase 6 (M6)
grid_points.success_method 已删除:值域混用列拆为 tlusty_success_method + synspec_success_methodM12/M13);整体归因派生 tlusty ?? synspec P9 拆分
tasks.max_relc 加注释"仅 TLUSTY 大气迭代有效"不改名(改名需动全链 SQL,收益低) Phase 7a

列名级残留(仅一处 + 一个耦合项)

  1. workflow_progress_snapshots.converged 列名:已随 7c 实施改名为 completed(M10)——存"管线完成点数"。
  2. grid_points.status'converged' 值:列名 status 无问题,是 TLUSTY-first(暗示大气收敛,实为管线完成)。7c 已实施:值改为 'completed'(M9 数据迁移 + 全链 SQL 字面量 + 前端)。

核实后无需改动的列

  • tasks.seed_point_name:DB 列只存 TLUSTY 热启动种子,synspec-only 派发时恒 Nonescheduler.rs:453);"双义"发生在 executor 局部变量 seed_atmos_path,不在 DB 列上,7a 注释即可。
  • grid_points.cno_sum / wave:存的就是 TLUSTY 丰度排序量,名实相符,7a 加物理语义注释。
  • tasks.elapsed_sec / attempt_count / atmosphere_ref / tlusty_* / synspec_* 家族:全部准确。

判定原则:DB 列改名成本高(迁移 + 全链 SQL + 前端 + 历史数据),仅在"列名本身误导"时才值得。本审计确认除 snapshots.converged(耦合项)外无列名级误导,其余均为列值/概念问题,走注释与派生口径解决。


10. 测试策略

沿用仓库现有惯例(db.rs / scheduler.rs 内 #[tokio::test] 模块 + tests/ 集成):

  1. 迁移测试:新库全流程;旧 schema 库升级;迁移中断恢复(Phase 0)。
  2. Phase 1record_task_report 全状态往返(含半失败、synspec-only、旧节点兜底)。
  3. Phase 2:索引生效(EXPLAIN)+ 既有统计测试不回归。
  4. Phase 3:新旧查询结果集逐行等价 + 分页计数不变。
  5. Phase 4:新库无 revoked 列;注册 → 审批 → 取 token 全链路(registration_secret 行为不变)。
  6. Phase 5:半失败点结算后 tlusty_status=convergedsynspec_status=failed;重试期间不覆盖 tlusty_statussynspec_success_method 归因正确。
  7. Phase 77a 前端过滤/统计对 synspec 收敛点等价断言;7b 改名后编译期全量通过。

每 Phase 合入前:全量 cargo test + cargo clippy -- -D warnings


11. 部署与回滚

  • 迁移安全性P1/P2/5a 的 ADD COLUMN / CREATE INDEX 全在线,无停写窗口; Phase 4M4)与 Phase 6M6)的 DROP COLUMN 涉及表重建,部署前做手动备份(已有每日 backup_database + 7 天保留兜底),在低峰窗口执行。
  • 回滚SQLite user_version 迁移无 down 迁移;回滚 = 从备份恢复 + 重放后续版本。ADD COLUMN 类变更对旧二进制无害(新列新代码写、旧代码不读)。
  • 部署顺序Phase 0 → 1 → 2 → 3 → 4 → 6 →(5a)→(5b)→(7a)→(7b),每阶段独立发版。
    • Phase 6 前确认队列为空(见 §8.8);
    • Phase 7a 无 schema 依赖,可与 Phase 1–4 并行实施;其统计/过滤修正与 5a 合并;
    • Phase 7b 改名在 Phase 6 之后(避免与 task_type 删除的改动冲突)。

12. 决策记录

决策 结论 依据
tasks 清理/保留策略 不做(仅加索引) 用户决策 2026-08-05:tasks 作为完整审计日志长期保留
nodes / node_credentials 不合并registration_secret 保留在 nodes(审批前状态),仅清 revoked 死列 写频差异显著;token_hash NOT NULL + 唯一索引决定 secret 只能存 nodes(审查 CRITICAL#1/#2
node_credentials.revoked 删除 死列,新代码不读写
grid_points.id AUTOINCREMENT 保留 一切按 (workflow_name, name) 访问,表重建不值当
Phase 5 立项,先 5a 后 5b 用户决策 2026-08-055b 待 summary_json 数据积累后评估
task_type 字段 全量删除Phase 6 用户决策 2026-08-05;集群无历史节点,随镜像统一重建,无旧节点兼容负担
Phase 7 命名语义修正 立项,先 7a(注释+前端)后 7b(改名),7c 已实施(2026-08-05 用户决策 2026-08-05subagent 全库审计确认 TLUSTY-first 命名残留