核心变更:
1. GridAxisValue 源精度命名
- 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
- config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
- runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
修复 REAL 列回读丢精度导致的 model_name 错配
2. 工作流执行可观测台
- 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
停滞预警、逐点明细分页、收敛性热力图数据)
- 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
- runner 携带 last_iter/worst_depth/n_depths 进 conv.json
- 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器
3. 节点停用/启用管理
- 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
移除 host_name 字段
4. 白名单结果归档
- 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
- executor 原子写入归档 + 200 点 LRU 上限
5. 历史数据导入
- sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
按新版命名迁移产物树
6. 部署与目录重规划
- data/results→seeds、data/archive→result + migrate_data_dirs.sh
- deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身
7. 文档同步更新 api/database/architecture/deployment
25 KiB
工作流执行可视化 —— 功能重设计文档
目标:把"恒星大气网格工作流"面板从当前的 3 个动作(查看 YAML / 启动 / 删除) 升级为完整的 执行可观测台:执行进度、逐网格点执行详情、收敛判定、 冷启动 vs 种子步进归因、参数空间冷启动成功图谱,以及失败点重试。
原则:数据已经在库里大半,缺口主要是 HTTP 暴露 + 少量持久化 + 全新前端 UI。 零新增前端依赖(不引入图表库),沿用现有 ESM + 原生 DOM + 设计令牌风格。
1. 现状盘点(带代码证据)
1.1 已经持久化、可直接用的数据
| 数据 | 位置 | 说明 |
|---|---|---|
网格点 6 维参数、cno_sum、wave |
grid_points 表(db.rs:246-263) |
按 workflow_name 分区,复合唯一 (workflow_name, name) |
点状态 pending/queued/running/converged/failed |
grid_points.status |
GridPointStatus(models.rs:235-243) |
成功手段 cold_run / seed_step / imported |
grid_points.success_method |
收敛时由成功任务的 task_type 回填(db.rs:1097-1101);失败点为 NULL |
| 重试次数 | grid_points.attempt_count |
每次回报 +1(db.rs:1089-1095) |
| 每次尝试的收敛指标 | tasks.max_relc |
收敛判据 max_relc < chmax(默认 0.001,conv_check.rs:105-109) |
| 每次尝试的种子来源 | tasks.seed_point_name |
派发时写入(scheduler.rs:194-205) |
| 每次尝试的方法 / 错误 / 节点 / 完成时间 | tasks.task_type / error_message / node_id / completed_at |
一个点多行(每次尝试一行),工作流删除前长期保留 |
| 全局种子库 | seeds 表 + data/seeds/<name>/<name>.7 |
仅收敛且无 NaN 的点入库(task.rs:187-207) |
| 逐阶段完整诊断(lte→nc→nl / seed_nc→nl) | data/seeds/<name>/conv.json |
ModelSummary(models.rs:376-391):每阶段 converged / best_max_relc / chmax / elapsed_sec、总耗时、synspec_sec、所用种子。成功与失败的点都会写(task.rs:178-184) |
| 按工作流的统计聚合 | db.get_grid_summary_stats(Some(wf)) |
已实现且有单测(db.rs:1582-1639),但没有任何 HTTP 端点调用它 |
1.2 缺口(需要补的)
| # | 缺口 | 影响 | 补法 |
|---|---|---|---|
| G1 | 无逐点 HTTP 端点 | 前端拿不到任何点级数据 | 新增 /workflows/:name/points |
| G2 | 无按工作流的统计端点(get_grid_summary_stats 未暴露;/api/status 只有全局) |
卡片/详情无法显示每工作流进度 | 新增 /workflows/:name/stats;列表接口内联计数 |
| G3 | queued 被并进 pending(db.rs:1596,1612) |
无法区分"排队中"与"未入队" | 新端点拆开统计 |
| G4 | 单点耗时/迭代数未进库(TaskReport.elapsed_sec 收到即丢;last_iter 在 StageSummary 前被丢弃) |
列表无耗时列、无 ETA | 迁移补列 + runner 携带 last_iter(Phase 3) |
| G5 | 无进度时间序列 | 画不出"进度-时间"曲线 | 新表 workflow_progress_snapshots(Phase 3) |
| G6 | 失败点无法重试(start 只重置 queued,db.rs:886-899) |
失败点永久卡死 | 新增 retry 端点(复用 reset_specific_grid_points_to_pending 思路) |
| G7 | conv.json 不提供下载/查看 | 逐阶段诊断无法展示 | 点详情端点读盘解析返回 |
| G8 | 前端无任何进度/详情 UI;无 tab 组件 | —— | 全新 UI |
1.3 领域语义(设计依据)
- 冷启动 vs 种子步进不是用户选的,是调度器按"种子可用性"自动决定的:
派发时
find_best_seed_from_db命中(同族精确匹配优先,否则全局加权距离 ≤ 3.0,seed_finder.rs:13-38)→SeedStep(热启动链seed_nc→nl),否则ColdRun(lte→nc→nl)。 - wave = 难度波次:按
cno_sum升序分组,"先易后难",让早期收敛点充当种子。 所以"哪些参数冷启动能成功" ≈ 低 cno_sum / 中低温区的点;高温 He-poor 区几乎全靠种子步进。 - 还有一次性失败回退:冷启动发散且存在邻居种子时,自动以
seed_step重投一次 (scheduler.rs:270-350,受seed_step_fallback配置与has_seed_step_attempt一次性闸门约束)。 因此success_method='seed_step'的点可能是"冷启动失败后被救回"的——该归因需从tasks多行历史还原。 - 第三类
imported:历史run_grid.py结果导入(db.rs:966-981),UI 需单独呈现为"历史导入"。 - 收敛判据:末次迭代最差深度点的最大相对修正
max_relc < chmax(默认 1e-3), 且大气 NaN 占比 ≤ 10%(conv_check.rs:127-149)。
2. 功能设计(信息架构)
2.1 工作流卡片(改造现有 workflows.js 卡片)
┌─────────────────────────────────────────────┐
│ sdB_cno [运行中] │ ← 现有:名称/描述/状态徽章
│ sdB 6维恒星大气模型计算网格 │
│ ▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░ 42% │ ← 新增:收敛进度条 + 百分比
│ 181/432 收敛 · 12 种子步进 · 3 失败 · 8 运行 │ ← 新增:一行计数摘要
│ [进入详情 →] [查看YAML] [暂停] [删除] │ ← 整卡头部可点进详情页
└─────────────────────────────────────────────┘
-
卡片名/头部区域是
<a href="#/workflows/sdB_cno">;操作按钮stopPropagation保持独立语义。 -
进度数据来自 列表接口内联计数(见 §3.2),不额外轮询。
-
状态徽章扩充:
initializing显示"初始化中"(warning 琥珀),paused显示"已暂停"(secondary), 不再把三者都归为"闲置"。
2.2 工作流详情页(独立页面,hash 路由 #/workflows/:name)
信息架构决策:详情内容量(三 Tab + 可过滤点表 + 热力矩阵 + 逐点诊断)远超模态框承载, 且需要深链/返回键/长时间监控驻留。因此采用独立视图页面 + 零依赖 hash 路由, 而非宽模态。入口:卡片整卡或"详情"按钮 →
location.hash = '#/workflows/<name>'。
页面骨架(顶部常驻"任务控制条",是全页最先映入眼帘的部分——直接呈现这个网格的实时生命体征):
← 返回控制台 sdB_cno [运行中] [启动/暂停] [重试失败] [查看YAML] [删除]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░ ← 分段进度条:绿=收敛 红=失败 琥珀=运行 蓝=排队 灰=待定
432 网格点 · 181 收敛(42%) · 8 运行 · 40 排队 · 3 失败 · 冷启动 169 / 种子步进 12 · ETA ≈ 2h40m
- 分段进度条按状态占比着色拼接,5s 轮询下各段宽度此消彼长——进度是"看得见在动"的。
- 计数用
tabular-num防跳动;数字变化可加轻量过渡。 - 工作流不存在(hash 指向已删除的名字)→ 空态卡 + 返回按钮。
Tab 1 — 执行概览
- 4 个 metric tile(复用
.metric-card):网格总数 / 已收敛 / 失败 / 运行中+排队。 - 方法归因:冷启动收敛 X · 种子步进收敛 Y · 历史导入 Z · 失败 N(彩色计数行)。
- wave(难度波次)分布:每波次
converged/total的小条形列表——直观看到"难度推进到哪了"。 - 最近动态流:最近 12 条尝试回报(点名 / 方法徽章 / 结果 / max_relc / 节点 / 完成时间),
轮询下新条目从顶部淡入——数据就是
points端点sort=last_completed_at&order=desc&limit=12,零新增端点。 - ETA(Phase 3):按平均单点耗时 × 剩余点数 ÷ 集群空闲槽位估算。
Tab 2 — 网格点明细
- 过滤器:状态(全部/排队/运行/收敛/失败)、方法(全部/冷启动/种子步进/导入)、wave 下拉、点名搜索框。
- 表格(复用
.data-table+ 关键行增量刷新,仿nodesTable.js):
| 列 | 来源 |
|---|---|
| 点名(mono 省略,title 全文) | name |
| Teff / logg / logHe / CNO和 | 6 维参数 |
| wave | wave |
| 状态 | badge:收敛=online 绿 / 失败=danger 红 / 运行=warning 琥珀 / 排队=info 蓝 / 待定=secondary |
| 方法 | badge:冷启动=蓝(blueprint)/ 种子步进=紫(purple)/ 导入=secondary——刻意区别于状态色 |
| max_relc | tabular-num,科学计数;失败点显示最后一次尝试值 |
| 尝试数 | attempt_count(>1 且收敛 = "被救回",加 ↻ 角标) |
| 耗时 | Phase 3 起有值;之前用 completed_at-created_at 粗估并标 ~ |
| 操作 | [查看] → 点详情 |
- 分页:
limit=100 + offset,服务端排序(默认 wave→cno_sum→teff)。 - 刷新策略:打开弹窗时拉一次;Tab 激活期间每 5s 自动刷新(可手动刷新按钮强制); 弹窗关闭即停。不污染全局轮询。
Tab 3 — 收敛性分析(冷启动图谱)
- 核心问题:"哪些参数范围内冷启动能成功"。
- 6 维空间降到 2D 热力矩阵:用户从下拉选 X 轴 / Y 轴(默认 X=Teff、Y=cno_sum); 其余维度若为多值则提供切片选择器(固定某维取值)。
- 单元格着色(CSS grid,无图表库):
- 🟩 冷启动收敛(
converged && method=cold_run) - 🟪 种子步进收敛(
seed_step) - ⬜ 历史导入(
imported) - 🟥 失败(
failed) - 🟨 运行/排队中
- ░ 未开始(
pending且从未尝试)
- 🟩 冷启动收敛(
- 矩阵下方:图例 + 聚合结论条("冷启动成功率 62%(268/432);Teff≥60000 区间冷启动成功率 0%,全部由种子步进救回 41 个、失败 3 个")。
- 单元格 hover 显示点名与 max_relc;点击 → 同 Tab 2 的点详情。
2.3 网格点详情(右侧滑入面板 slide-over,不再模态套模态)
点表格/热力图/动态流中任一点名 → 右侧滑入 480px 面板(.point-panel,背景遮罩 + Esc/点击遮罩关闭,
复用焦点圈管理;独立页面上滑入面板比二级模态更轻、视线不跳转)。
- 头部:点名(mono)+ 状态/方法徽章 + [复制点名]。
- 尝试历史表(
tasks多行):# / 方法 / 种子来源(点名,可点击跳查该点)/ max_relc / 结果 / 节点 / 完成时间 / 错误摘要。 这一张表直接还原"冷启动失败 → 种子步进救回"的完整剧情。 - 逐阶段链诊断(读
conv.json):每阶段一张小卡:lte✅ grey start ·nc⚠️ max_relc=0.957(不要求收敛)·nl✅ max_relc=6.9e-3 / 17 iter / 612s; 种子步进链则显示seed_nc → nl,并标注种子点名与总耗时、synspec 耗时、NaN 标志。 - 失败点:突出显示最后一次
error_message(code-pre-wrap,textContent 渲染防 XSS)。
2.4 视图与操作逻辑
路由(新增 src/router.js,约 70 行,零依赖):
| hash | 视图 | 挂载动作 | 卸载动作 |
|---|---|---|---|
#/(或空/未知) |
控制台首页(现有布局) | 渲染骨架 + startPolling() |
停全局轮询 |
#/workflows/:name |
工作流详情页 | 渲染页面骨架 + 拉 stats/points + 启作用域轮询 | 停作用域轮询 |
window.addEventListener('hashchange', render);首次加载按当前 hash 渲染。- 登录门禁不变:无 token 时任何 hash 都先展示登录遮罩,登录成功后按 hash 进入对应视图。
- 全局 header/footer 保持常驻;视图内容渲染进
#view-root容器(index.html结构调整: 现有main.main-container内容收敛为首页视图模板)。 - 返回键/深链/刷新天然可用(hash 不触发整页刷新)。
交互一览:
| 操作 | 前端 | 后端 |
|---|---|---|
| 进详情页 | hash 切换 → 按需 fetch stats + points(第1页) | —— |
| 切 Tab | 委托 data-wf-tab;切到"明细/分析"时惰性加载 |
—— |
| 过滤/翻页/排序 | change/click → 重新 fetch(带 query) | 端点支持参数 |
| 自动刷新 | 详情页挂载期间 5s 拉 stats(控制条+概览常驻刷新);明细 Tab 激活时同步刷新表格 | —— |
| 点详情面板 | 行内 [查看]/点名链接 → 拉 points/:point → 滑入面板;种子来源点名可跳查(换载面板内容) |
—— |
| 重试失败点 | showConfirm → POST → toast → 刷新 stats/points |
新端点,仅 running 状态允许 |
| 返回首页 | "← 返回控制台"链接 / 浏览器返回 | —— |
| 首页卡片 | 整卡点击进详情;卡片上保留 启动/暂停/删除 快捷操作 + 查看YAML(仍为模态) | —— |
3. 后端设计
3.1 新增端点(全部挂在 /api/workflows/:name/... 下)
鉴权:
required_role里path.starts_with("/workflows/")已映射 Admin(mod.rs:92-95), 子路由无需改鉴权矩阵。统一返回{success, message, data}信封(与现有 workflow API 一致)。 点名参数复用现有字符集白名单校验(task.rs:133-146同款),防路径穿越。
① GET /api/workflows/:name/stats —— 每工作流进度(G2/G3)
复用并增强 get_grid_summary_stats(Some(wf)),拆开 pending/queued,补方法与波次维度:
{
"success": true, "message": "ok",
"data": {
"name": "sdB_cno", "status": "running",
"total": 432,
"pending": 120, "queued": 40, "running": 8, "converged": 261, "failed": 3,
"cold_run_converged": 220, "seed_step_converged": 41, "imported_converged": 0,
"waves": [ {"wave": 0, "total": 108, "converged": 108, "failed": 0}, ... ],
"avg_point_sec": 740.0, "eta_sec": 9600
}
}
avg_point_sec/eta_sec:Phase 3 有elapsed_sec列后精确计算; 之前用tasks.completed_at - tasks.created_at均值近似(含排队等待,偏大,标注近似)。- SQL 走现有覆盖索引
idx_grid_points_wf_status,单次聚合扫描,5s 轮询无压力。
② GET /api/workflows/:name/points —— 逐点列表(G1)
参数:status、method、wave、q(点名子串)、sort(wave|teff|max_relc|attempts,默认 wave)、
order、limit(≤500,默认 100)、offset。
{
"success": true, "message": "ok",
"data": {
"total": 432,
"points": [
{
"name": "t60000_g5.0_he-2_c-4_n-4_o-4",
"teff": 60000, "logg": 5.0, "loghe": -2, "logc": -4, "logn": -4, "logo": -4,
"cno_sum": -12, "wave": 0,
"status": "converged", "success_method": "seed_step", "attempt_count": 2,
"last_max_relc": 0.00043, "last_task_type": "seed_step",
"seed_point_name": "t60000_g5.0_he2_c-4_n-4_o-4",
"node_id": "node-a1b2", "last_completed_at": "2026-07-30T11:12:00Z",
"elapsed_sec": 126.4
}
]
}
}
- "最近一次尝试"用关联子查询取
tasks最新行:
SELECT gp.*, t.max_relc AS last_max_relc, t.task_type AS last_task_type,
t.seed_point_name, t.node_id, t.completed_at AS last_completed_at,
t.error_message AS last_error
FROM grid_points gp
LEFT JOIN tasks t ON t.task_id = (
SELECT t2.task_id FROM tasks t2
WHERE t2.point_name = gp.name AND t2.workflow_name = gp.workflow_name
ORDER BY t2.completed_at IS NULL, t2.completed_at DESC, t2.created_at DESC
LIMIT 1)
WHERE gp.workflow_name = ?1 [AND ...filters]
ORDER BY gp.wave, gp.cno_sum, gp.teff
LIMIT ? LIMIT OFFSET ?;
- 配套新索引:
CREATE INDEX idx_tasks_point_wf_time ON tasks(point_name, workflow_name, completed_at);(迁移中补;432 点规模下无索引也能跑,加索引是为大网格兜底。) - 备选(若大网格性能不够):把
last_*字段反规范化进grid_points,在record_task_report里顺手写—— 但默认不做,避免迁移面扩大;先用 JOIN,压测后再议。
③ GET /api/workflows/:name/points/:point —— 单点详情 + 阶段诊断(G7)
{
"success": true, "message": "ok",
"data": {
"point": { /* 同 ② 的单点字段 */ },
"attempts": [
{"task_id": "...", "task_type": "cold_run", "seed_point_name": null,
"status": "failed", "max_relc": 954000.0, "atmosphere_has_nan": true,
"node_id": "node-a1b2", "error_message": "nl stage diverged...",
"created_at": "...", "completed_at": "..."},
{"task_id": "...", "task_type": "seed_step", "seed_point_name": "t60000_...",
"status": "completed", "max_relc": 0.00043, ...}
],
"conv": { /* data/seeds/<name>/conv.json 解析后的 ModelSummary;文件不在则 null */ }
}
}
conv读盘路径严格拼为seeds_dir/<point>/conv.json(单层目录,point 先过白名单 + canonicalize 归属兜底),解析失败/不存在 →null(前端降级为"诊断文件不可用")。 不把整文件塞进列表接口(体积控制)。
④ POST /api/workflows/:name/points/retry —— 失败点重试(G6)
请求体:{"names": ["t80000_..."], "all_failed": true}(二选一)。
- 前置:工作流必须
running(否则 400,提示"请先启动工作流"——避免重置后无调度器消费的僵尸态)。 - 逻辑:新 db 函数
reset_failed_points(wf, names|all):UPDATE grid_points SET status='pending', success_method=NULL WHERE workflow_name=? AND status='failed' [AND name IN (...)], 随后立即触发一次schedule_pending_tasks()(不必等 30s tick)。 - 重试后的方法仍由调度器按种子可用性自动决定(大概率 seed_step,因为此时种子池已更丰富)—— 这与领域语义一致,前端文案如实说明:"重试的点将由调度器自动选择冷启动或种子步进"。
⑤ 列表接口内联计数(G2,卡片进度条用)
GET /api/workflows 的 WorkflowSummary 追加:
{"name": "...", "status": "...", "description": "...",
"created_at": "...", "updated_at": "...",
"stats": {"total": 432, "converged": 261, "failed": 3, "running": 8,
"cold_run_converged": 220, "seed_step_converged": 41}}
- 实现:
list_workflows里对grid_points做一次GROUP BY workflow_name聚合再 map 合并(单条 SQL,无 N+1)。 - 未启动(无网格点)的工作流
stats: null,前端不显示进度条。
3.2 持久化变更(Phase 3,最小迁移)
Phase 1/2 零迁移——全部基于现有列。以下为 Phase 3 的可选增强:
grid_points加列:elapsed_sec REAL(最近一次成功/失败尝试的墙钟耗时)。 在record_task_report写入——TaskReport.elapsed_sec本来就在手(models.rs:318),当前被丢弃。StageSummary携带last_iter / worst_depth / n_depths(models.rs:364-373扩字段,runner.rs:302-317处ConvCheckResult已有值,只是没搬过去)→ conv.json 与点详情获得迭代数。- 新表
workflow_progress_snapshots(workflow_name TEXT, ts DATETIME, pending INT, queued INT, running INT, converged INT, failed INT):由main.rs:111-197的 30s 后台循环顺手写一行; 保留策略:仅当计数相对上次快照有变化才写;定期清理 24h 前的行。供"进度-时间"曲线与精确 ETA。
3.3 明确不做 / 保持现状
- 不改 pull 调度模型、不改 wave 语义、不改种子匹配算法——只读取与展示。
- 不引入 WebSocket/SSE:5s 轮询 + 详情弹窗作用域轮询足够,复杂度与收益不匹配。
- 不给
/api/seed/:name开 Admin 权限(它是 Node 角色的二进制通道);前端只需 conv.json 的 JSON 内容,走 ③ 即可。 seeds表/.7文件不暴露列表下载(超出本次范围,且属 Node 域)。
3.4 安全清单(对照全局安全规约)
- 所有新端点经现有
auth_middleware(Admin 角色)+ 全局限流;无新增鉴权旁路。 - 点名/工作流名全部过字符集白名单(
[A-Za-z0-9._-]);conv.json 读盘路径不接受../绝对路径。 - 无新增密钥;错误信息用现有
AppError(Internal 不回显细节,error.rs:28-34)。 - 前端所有服务端字符串经
escapeHtml(列表/表格)或textContent(conv.json、错误文本)渲染。 - retry 端点为写操作:仅 Admin、受限于 running 状态、参数白名单,无注入面(参数化查询)。
4. 前端实现设计
4.1 新增/改动文件
| 文件 | 改动 |
|---|---|
src/router.js(新) |
hash 路由:parseHash() → {view:'home'|'workflow', name?};hashchange 监听 + 首载渲染;视图挂载/卸载生命周期钩子(停/启作用域轮询);登录门禁判断 |
src/views/home.js(新) |
现 index.html 中 main.main-container 的首页内容收敛为模板函数 + 挂载逻辑(metric 卡/节点表/工作流卡列表),startPolling() 在此触发 |
src/views/workflowDetail.js(新) |
详情页:任务控制条(分段进度条/计数/ETA/操作)+ Tab 容器;概览(tiles/方法归因/wave 分布/最近动态流)、点表格(关键行签名 diff,仿 nodesTable)、过滤器、热力矩阵(CSS grid 单元格)、点详情滑入面板。所有插值 escapeHtml;作用域 5s 轮询自管 |
src/api.js |
新增 fetchWorkflowStatsApi(name)、fetchWorkflowPointsApi(name, params)、fetchPointDetailApi(name, point)、retryPointsApi(name, body);全部经 apiFetch + encodeURIComponent,返回 raw Response |
src/components/workflows.js |
卡片加进度条/计数/状态徽章扩充;头部包成进详情的锚链接;读取列表内联 stats;空 stats 降级 |
src/main.js |
瘦身为全局接线:登录/主题/登出/全局模态(create-wf / view-yaml / reissue / confirm)/ header 刷新;视图级委托移入各 view |
src/state.js |
全局轮询仅服务首页;视图切换时由 router 停启 |
index.html |
main 收敛为 <div id="view-root">(视图由 JS 渲染);保留登录遮罩/header/footer/全局模态/toast;新增 #point-panel 滑入面板骨架 |
src/style.css |
新组件:.wf-status-strip(控制条 + 分段进度条 .seg-progress 五色段)、.wf-tabs(tab 栏,双主题)、.method-badge.cold/.seed/.imported(蓝/紫/中性,区别于状态色)、.heatmap/.heatmap-cell(状态色填充 + hover)、.stage-chip(阶段诊断小卡)、.point-panel(右侧滑入 + 遮罩)、.activity-feed(动态流条目淡入);全部走设计令牌 + prefers-reduced-motion 降级 |
4.2 必须遵循的现有约定(探查结论)
- 事件一律
data-*委托,不挂逐按钮监听(main.js:377-388模式)。 - 服务端字符串进
innerHTML前必过escapeHtml;整块文本用textContent。 - 模态:静态骨架 +
setupFocusTrap,close 按钮类modal-close(否则 Escape 映射失效,modal.js:21)。 - 破坏性操作先
showConfirm,反馈只走showToast,变更后fetchAllData()立即刷新。 - 轮询下渲染:行/卡 keyed by
data-*+ 签名 diff + 高频单元格textContent原位刷新(nodesTable.js:34-54模式),空态写入做 trim 比较防抖。 - 样式只用令牌;状态复用
.status-badge变体;进度复用.progress-bar-track/-fill;表格复用.data-table+.col-*+.tabular-num。 - 工作流标识是
name(无数字 id);后端状态含initializing,前端要显式映射。
5. 分期实施计划
Phase 1 — 进度可见(MVP,零迁移)
- 后端:
stats端点(拆 queued)+points端点(JOIN 最近尝试 + 新索引)+ 列表内联stats。 - 前端:hash 路由 + 视图化重构 + 卡片进度条/计数/徽章 + 工作流详情页(任务控制条 + 「概览」「网格点明细」两 Tab + 过滤器/分页 + 最近动态流)。
- 验收:深链
#/workflows/sdB_cno打开详情页,能看到 432 点逐行状态、每点方法/max_relc/尝试数/种子来源,进度条随轮询而动。
Phase 2 — 诊断与归因
- 后端:
points/:point端点(尝试历史 + conv.json 解析)+retry端点 + dbreset_failed_points。 - 前端:点详情滑入面板(尝试历史 + 阶段链诊断 + 错误)+「收敛性分析」热力 Tab + 「重试失败点」按钮。
- 验收:能回答"这个点为什么失败/被谁救回",能看到冷启动成功参数区,能一键重试失败点。
Phase 3 — 时序与精算(可选增强)
- 后端:迁移(
elapsed_sec列、StageSummary.last_iter)+workflow_progress_snapshots+ ETA 精算。 - 前端:概览 ETA 精确化 + 进度-时间 sparkline + 点表耗时列去"~"。
6. 待确认的开放问题
- Phase 范围:三期全做,还是先交付 Phase 1+2?
- 「重试失败点」是否需要(它会触发真实计算任务)?默认仅 running 时可用。
- 热力矩阵默认轴 Teff × cno_sum 是否符合你的物理直觉?(sdB_cno 当前网格 logg/logHe/CNO 各只有单值,实际是 Teff 一维——多值网格下矩阵才有意义。)
- conv.json 之外是否还需要在点详情里提供逐阶段
.err/fort.9原始文件下载(目前节点归档data/result有 LRU 200 上限,服务端只有 conv.json)?