Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ ADR-147:Terminal正常实际提交“你”及wq前缀确认后的failure/INT/
- [x] `TBD-3` ADR-029:用户词库TSV v1
- [x] `TBD-4` 英文/包名最终形(ADR-056:Lampyris)

- [x] **更新器自更新验收(2026-11-05 闭环,报告 226 / ADR-306)**:1.0.0→1.0.1 演练暴露 `stopHost` 把正在自更新的进程 SIGTERM 掉;1.0.2 修复后真机复验:回执 `payloadSHA256=732756e5…`、更新程序 bundle 被替换且旧版备份、staging 清理、进程未自杀。
- [ ] IT-25:ADR-159五笔主动后项学习工程已通过隔离回归、make verify/strict universal/Swift FFI;待新签名原生验收与用户例子确认。见docs/tasks/T-ITER-25.md。

2026-10-14报告011:21个隔离默认配置FFI样例与Apple/Rime/Fcitx官方对照,新增待决IT-29混输提交歧义、IT-26兜底首屏、IT-27隔音符/读音质量与IT-28编辑/隐私补验;无产品代码修改。IT-25本地提交先保存,整体UI/公开推送继续暂缓。
Expand Down
1 change: 1 addition & 0 deletions docs/MVP后执行计划.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
| **B 基线采集** | 无发布 | 150–200 条个人高频样本集(单字/两字词/混排长句)+评测脚本;空闲功耗、冷加载基线 | **不需要换包**,可在 1.0.0 上直接做,且必须在改排序**之前**取得 | 无(可与 A 并行) | 脚本入库、基线 JSON 入库、能在任意包上一键复跑 |
| **B2 设置一致性** | **1.0.1** | ADR-305:`input.full_width` Bool→枚举、⌥F 改为持久化(与 ⌥P/⌥T 对齐)、旧布尔配置迁移 | 单一行为面(设置项一致性),改动小、可直接出补丁;顺带成为 1.0.0→1.0.1 的升级练手,闭环更新器自更新 | A | 面板按 ⌥F 立即跟随、四项控件同构、迁移单测通过、升级路径 PASS |
| **C 排序质量包** | **1.1.0** | ① D17 二级简码表 + 一级 25 条入表(ADR-304 P0)② `personalize` 改有界位次上移(P1)③ IT-25 原生验收 ④ IT-22 受影响行复跑 ⑤ **更新器自更新闭环**(1.0.0 → 1.1.0 由 build 34 更新程序执行) | 全部落在"候选顺序"这一个行为面上,一次换包、一次回归;③④⑤ 本来就要求"新签名包装机复验" | B 的基线与脚本;D17 数据许可登记 | 简码 25/625 校验通过、6 条新单测、样本集四项指标不劣化且关键项改善、两台设备同包、升级路径 PASS、IT-25/IT-22 受影响行 PASS |
| **B3 记账小修** | 随下一版 | 回执补 `updaterBundle`(报告 226 §6)、ADR-304 相关文档 | 已实现待发,随 1.1.0 一起出 | — | 回执字段可见、离线回归通过 |
| **D 排序观察期** | 1.1.x 补丁 或 1.2.0 | ADR-304 P2:误选不学习、时间衰减、MRU;P3:位置记忆/「固定顺序」开关 | 只有 C 之后样本集仍显示明显非首选率才做;**一次一项**,便于归因 | C 的样本集对比数据 | 单变量对比显示改善,且无"越用越乱"回归 |
| **E 拼音深化** | 独立立项 | 拼音后端质量评估 → 是否自研 FSA 的 ADR → 整句/长词(很可能不做) | 风险面与五笔排序无关,不能搭车 | 质量评估报告 | 有证据支持才立 ADR |
| **F 平台扩展** | 独立项目 | R3 Windows(TSF spike 限 1–2 周,失败退 C++ 薄壳)→ R4 Linux → R5 移动端 | 各平台宿主互不影响,但会分散维护精力,**串行** | macOS 维护有余力 | 各自平台真实应用验收 |
Expand Down
28 changes: 28 additions & 0 deletions docs/reviews/226-self-update-kills-itself.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,31 @@ receiptsExists = true
- `install-core.swift` 新增两条断言(生产停、自更新不停)
- 真机复验:见本报告后续追加(将在 1.0.2 发布后执行:更新程序 ≥ 宿主版本的一次安装,
检查回执写入、staging 清理、更新程序被替换且进程不再自杀)

## 5. 真机复验(2026-11-05,air,1.0.2 / build 37)

做法:装 1.0.2(宿主与更新程序都是 37,含修复),用仓库里既有的**演练专用**比较基准
`LAMPYRIS_UPDATE_CURRENT_BUILD=30` 启动更新程序,使它认为"有新版本 37",从而走完整安装 + 自更新分支
(`WebsiteReleaseConfiguration.currentBuild` 注释即为此用途)。

在更新程序窗口里点「安装更新」后核对:

| 项 | 结果 |
|---|---|
| 回执写入 | `updates/receipts/2026-11-05T10-11-57Z.json` ✅ |
| `payloadSHA256` | `732756e5…` = **已发布的 1.0.2 ZIP** ✅ |
| 宿主执行 | `b0b3bf5c…`(1.0.2/37)✅ |
| 旧宿主备份 | `upgrade-2026-11-05T10-11-50Z-…/backup/Lampyris.app` ✅ |
| **更新程序被替换** | `~/Applications/LampyrisUpdateCheck.app` mtime = 负载内同名 bundle 的 mtime(17:35:44),旧版在 `.lampyris-updater-backup-2026-11-05T10-11-54Z`(4.3 MB,build 37)✅ |
| **进程未被自己杀掉** | 安装持续到写完回执(10:11:54 替换 → 10:11:57 回执)✅ |
| staging 清理 | `upgrade-…/` 只剩 `backup/`;`~/Applications/.lampyris-install-*` 无残留 ✅ |
| 用户数据 | config `5d4271fb…`、词库 1124 行,与升级前一致 ✅ |

**结论:ADR-306 的修复经真机验证通过;长期挂着的「更新器自更新」验收项闭环。**

## 6. 同批发现的两处记账/门禁问题(已修或已排期)

1. **回执漏记自更新结果**:`ReceiptPayload` 没有 `updaterBundle` 字段,导致本次只能靠 mtime 与备份目录反推"更新程序真被替换了"。
已在源码补上该字段并加断言(`install-core.swift` 7b 段),随下个版本发布。
2. **发布工作流取产物失败**:`gh run list --commit X --status success` 对 1.0.2 的提交返回空,而两个条件单独都正常;
改为只按提交过滤、在 jq 里判 `completed && success`(PR #220)。
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,17 @@ public enum UpdateInstaller {
let previous: String?
let candidateExecutableSHA256: String
let installedExecutableSHA256: String
/// 自更新把更新程序换到哪、旧版备份在哪;没做自更新时为 nil。
///
/// 之前漏了这一列,导致真机演练里"更新程序确实被替换了"只能靠 mtime 与备份目录反推(报告 226 §5)。
let updaterBundle: String?
init(receipt: InstallReceipt) {
payloadSHA256 = receipt.payloadSHA256
signature = receipt.signature
installed = receipt.installed
previous = receipt.previous
candidateExecutableSHA256 = receipt.candidateExecutableSHA256
updaterBundle = receipt.updaterBundle
installedExecutableSHA256 = receipt.installedExecutableSHA256
}
}
Expand Down
16 changes: 16 additions & 0 deletions platforms/macos/tests/install-core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ let signature = try! signingKey.signature(for: payloadBytes).base64EncodedString
let publicKey = signingKey.publicKey.rawRepresentation.base64EncodedString()

let receiptSupport = root.appendingPathComponent("support-with-file-receipts")
// 7b) 回执必须记录自更新结果(报告 226 §5:漏了这列,真机只能靠 mtime 反推)
let receiptOK = root.appendingPathComponent("receipt-ok")
try! FileManager.default.createDirectory(at: receiptOK, withIntermediateDirectories: true)
_ = try! UpdateInstaller.install(
payload: payload, signature: signature, publicKeyBase64: publicKey,
domain: receiptOK, support: root.appendingPathComponent("receipt-ok-support"),
environment: environment(expect: "payload-host", executable: "Lampyris"),
selfUpdate: UpdateInstaller.SelfUpdate(
directory: applications,
environment: environment(expect: "payload-updater", executable: "LampyrisUpdateCheck")))
let receiptFiles = try! FileManager.default.contentsOfDirectory(
at: root.appendingPathComponent("receipt-ok-support/receipts"), includingPropertiesForKeys: nil)
require(receiptFiles.count == 1, "one receipt per install")
let receiptJSON = String(decoding: try! Data(contentsOf: receiptFiles[0]), as: UTF8.self)
require(receiptJSON.contains("\"updaterBundle\""), "receipt must record the self-updated updater path")

try! FileManager.default.createDirectory(at: receiptSupport, withIntermediateDirectories: true)
try! Data().write(to: receiptSupport.appendingPathComponent("receipts")) // 目录位置被文件占用

Expand Down
Loading