myastro 第二阶段路线图(E 系列)¶
版本:1.0|前置文档:capabilities.md(领域能力清单,最高规划文档)、design-io.md(io 层设计)、dev-plan.md(第一阶段 M1–M7,已完成)
0. 定位与背景¶
第一阶段交付了 io 层(统一作业基底、双执行器、账本、TUI 雏形)。第二阶段的驱动需求:
- 执行器(python/aria2)泄漏为用户概念,割裂;python 源无法在 TUI 下单
- 源完善度不足(TUI 可下单的实际只有 legacy)
- 只有单文件下载,天文数据的基本形态是批量
- 本地/云端需要共管:本地不全量持有,但需要时可下载、随时知道本地有什么
顶层约束(本项目级,非 top 级):myastro 是一个库,top 只是 GUI 客户端。 内核必须有良好的公开 API 供第三方调用;讨论以 top 展开时,设计与编码 不得局限在 top 视角。
1. 分层架构¶
L3 客户端 TUI(top) · CLI · notebook · 第三方程序 ← 平等的四类消费者
L2 API 门面 import myastro → fetch/query/status/… ← 稳定公开接口
L1 内核 任务注册表(源描述符) · 作业引擎(双执行器) · 账本(db) · 索引
L0 设施适配 aria2 / astroquery / textual ← 全部可替换
第一阶段的问题用这把尺子度量:
- 无门面:内部路径(myastro.io.sources.tess)即事实 API,重构即破坏
- JobSpec 携带 fn 闭包:非数据、不可序列化——CLI/TUI 之外的进程无法凭 JSON 创建作业;跨进程 retry 因此受限
- 注册表半数据半展示文案:没有按"参数 schema"设计过
2. 内核契约:源描述符¶
内核唯一核心概念,一个抽象服务四个客户端:
@dataclass
class FieldSpec:
name: str
type: type # int/float/str/Enum
default: Any = None
label: str = "" # 展示名(TUI 用,内核不依赖)
class SourceDescriptor:
name: str
fields: list[FieldSpec]
product: str # 产物格式(TYPES 键)
def run(self, params: dict) -> JobSpec
杠杆点:一份 fields 声明派生五件事——TUI 动态表单、CLI 参数 (argparse 自动生成,终结手写命令分发)、参数校验、跨进程持久化 (name+params 即 JSON)、API 参考文档(doc-sync 同思路:文档=渲染产物)。
python 源的 run 产出具名任务(TASKS 注册表:名字→顶层函数), 不产闭包——fn 不可序列化的老限制随之消失。
3. 架构不变量(测试守卫)¶
top 不得 import 内核私有路径;top 做到的任何事,第三方经由公开门面 都能做到。
守卫方式:测试静态检查 top/ 的 import 只指向门面模块。top 是 API 的 狗粮测试——GUI 用着别扭处即 API 设计缺陷。
4. 阶段计划¶
E0 门面与契约(最优先,地基)✅ 已完成(0.2.0)¶
- ✅ FieldSpec / SourceDescriptor 类型化;6 源迁入描述符(包装策略, 源实现重写留待逐源迁移)
- ✅ 内核任务具名注册(TASKS);跨进程 python retry 补齐
- ✅
myastro门面包:fetch/query/sources/describe/register_source/ status/wait/retry/delete/index 系列,__all__即承诺 - ✅ "top 不得导入内核私有符号"守卫测试(首次运行即抓到 _fmt_size 违规)
- ✅ CLI
fetch子命令参数从描述符自动生成 - 错误契约:门面异常体系化(参数错误 / 网络失败 / 数据不存在 / 状态冲突分类,第三方可 catch 区分)——替代现状散落的异常
- 产物 schema 稳定性:fetch 返回文件的列 schema(如光变 parquet 的
time/flux/flux_err/quality)对第三方是契约,列入
__all__同级的 稳定承诺;变更走版本化(schema_version 字段)
E1 双执行器隐身(E0 的自然结果)✅ 已完成¶
- ✅ 执行器从用户视野消失:info 面板执行器列移除(详情保留通道说明)
- ✅ TUI 新建表单全源通用(描述符驱动):tess/gaia/ztf-meta(python 任务) 与 ztf/legacy/unwise(URL 构造)同一下单表单;FieldSpec 补 optional 语义
- ✅ 表单预览对 python 源显示"任务+参数+产物",URL 源显示"URL+落盘路径"—— 预览文本本身不再出现执行器字样
E2 源完善(可与 E1 后半并行)¶
| 优先级 | 源 | 状态 |
|---|---|---|
| 高 | mpc、simbad、vizier | ✅ 已实现(E2 首批;simbad 有低概率原生段错误, |
| 单次重试恢复,note 已记) | ||
| 高 | skyfield | 待做:DE440s 历表本地化(jpl.nasa.gov 不可达,需找镜像) |
| 中 | panstarrs | 受阻:ps1catalog 端点已迁移(404),需考古新 API |
| 用户动作 | ztf 全面解锁 | 等用户注册 IRSA token |
| 待恢复 | asassn、unwise | 定期重试;实现已就位 |
E3 批量下载 ✅ 已完成¶
- ✅ jobs 表
batch_id(migration v4)+jobs.submit_batch+ 门面ma.batch(source, param_rows)+ma.status(batch_id=...) - ✅ TUI 批量下单屏(
b键):源选择 → 前缀过滤(上限 300 渲染)→ SelectionList 多选(空格勾选)→ Ctrl+S 整批提交;实测批次 b21a301cd 走通 - ✅ 传输并发由 aria2 原生承担
E4 本地/云端共管视图(部分随 E3 提前交付)¶
先划边界:"随时知道云端有什么"不存在全局解(云端全集无限大)。 正确模型:
云端认知 = 每源可枚举目录(索引:brick 366k、field 列表…)
本地事实 = astro.db 账本
共管视图 = 索引 × 账本 join:条目 → ✓ 已有 / ☁ 仅云端(可一键下)
- ✅ 批量下单屏的选项标签已带 ✓本地/☁云端 标记(E3 顺带交付)
- ✅ "需要时下载" = 在同一屏勾选 ☁ 条目 Ctrl+S(按需下载即达成)
- ✅ 覆盖率统计(本地/全量):
ma.coverage(index)+ CLImyastro coverage,实测 legacy-bricks 7/366912 - 不可枚举源(gaia/ztf 查询型)的云端认知仍走查询 API = notebook 领地, TUI 不做网络检索的边界不变
5. 稳定性与依赖策略¶
myastro.__version__语义化版本;门面符号改动走 deprecation- io/ 以下内核私有,重构自由
- 不引入 pydantic/click/typer:FieldSpec 用 dataclass 手写(零多余依赖), CLI 生成与描述符合一,功能重复的轮子不装
- io 层作为整合层有存在正当性(查询=astroquery、传输=aria2、UI=textual 各司其职;DVC/git-lfs 类工具不适用——云端是异构公共档案非单一 remote)
6. 纪律(延续第一阶段)¶
测试全绿才提交 · 注册表声明式扩展 · 身份寻址 · 文档=渲染产物 (doc-sync 纳入新注册表)· 每阶段结束做一次架构 review(对照本文档)。