Add the project rules, requirements and task records
This commit is contained in:
252 files changed
+3829
-8
No files matched your search
+177
@@ -0,0 +1,177 @@
|
||||
# Tasks
|
||||
|
||||
进度真相。自上而下,逐条完成,不跳项。编号前缀是 PRD 章节号,`C00` 是用户指令的前置项。
|
||||
|
||||
每条完成后:`scripts/review.sh ID 轮次` 在 herdr 开新分栏,codex --yolo(gpt-6.1-sol,effort high)审查,最多三轮。有问题先修,复审通过才勾选。审查只看缺陷,不收“建议增强”。每轮结论写入 `tasks/reviews/<编号>-r<轮>.md`。
|
||||
|
||||
状态:`[ ]` 未做 `[x]` 完成且审查通过 `[~]` 三轮用尽,末轮缺陷已修并有回归测试,未经复审 `[!]` 外部阻塞,写明原因与证据,不算完成。
|
||||
|
||||
## C00 前置
|
||||
|
||||
- [x] C00.1 工具链:cmake、ninja、pkg-config、git-lfs 就位;`scripts/doctor.sh` 打印版本,缺项以非零码退出 — R3 ok
|
||||
- [x] C00.2 上游源码:`scripts/fetch-references.sh` 全部 pin 检出,`reference/*` 的 HEAD 等于 pin — R2 ok
|
||||
- [x] C00.3 组件库研读:可复用组件、缺口、gpui 规则写入 `agents.md` — R3 ok
|
||||
- [x] C00.4 引擎前置验证(§23):LOK 动态库加载与转换,MuPDF 构建并渲染一页,CEF 分发包可取,MailCore 2 依赖可构建。结果与阻塞项写入 `docs/engineering-risks.md`;官方 macOS 包出不了图块(R-01),出图块的证明改在 C07.1 — R5 ok(codex 复审)
|
||||
|
||||
## C01 产品定义与平台(§1–§4)
|
||||
|
||||
- [x] C01.1 workspace 骨架(§17.1):八个 app,加当前用到的 crate(`ely-ui`、`ely-platform`;其余在各自任务建,不留空 crate);AGPL 许可字段;`cargo check --workspace` 通过;每个 app 开出一个 Ely 主题窗口;`ELY_SMOKE=1` 启动即退出的冒烟脚本 — R2 ok
|
||||
- [x] C01.2a 打包(§4.1、§4.2):macOS `.app` 脚本,Linux AppImage 配方,Windows 安装脚本;卸载保留用户目录。本机验证:macOS 包构建、签名、启动;Windows 脚本经 makensis 编译;Linux 桌面文件经校验。未验证:AppImage 构建、Windows 安装与卸载的运行、正式签名与公证 — R4 ok(codex 复审)
|
||||
- [x] C01.2b 手动检查更新、下载、离线校验;包管理器安装的版本不自行更新(§4.2)。本条只做 `ely_platform::update`;更新前保存编辑的提示与界面在 C05.4、C06.4 — R3 ok
|
||||
- [x] C01.2c 图形后端初始化失败时显示原因,不留空白窗口(§4.1)。已验证(macOS):失败信息写日志、打到标准错误、弹系统对话框,以 1 退出;GPUI 启动回调里的 panic(macOS 上会 abort)经 panic 钩子走同一条路,首帧之后的 panic 仍是普通 panic;启动前探测 Metal 设备。未验证:没有 Metal 设备的机器(探测失败分支)、`open_window` 返回错误的分支(macOS 只对弹出窗口返回错误)、Linux 与 Windows 的后端失败(未探测,本机无 Linux C 交叉工具链) — R3 ok
|
||||
|
||||
## C05 统一 UI/UX(§5)
|
||||
|
||||
- [x] C05.1 主题:浅色、深色、跟随系统,Omarchy Theme Sync,动效 token 与减少动效。已验证(macOS):Omarchy 自带的 22 套色板(`reference/omarchy`,pin f45461a)映射后满足组件库 A11yChecker 的全部规则(正文与状态色 4.5:1,焦点与图表色 3:1);`colorN` 形式的生成主题经回退链得到色相;运行中的应用按 Omarchy 的替换顺序跟随,无 `colors.toml` 时退回;覆盖开关与减少动效进入应用。Linux 与 Windows 的减少动效读取通过了目标编译(x86_64-unknown-linux-gnu、x86_64-pc-windows-msvc),未运行。未验证:Linux 上 Omarchy 默认目录的探测、gsettings 与 Windows 调用的实际结果、真实 Omarchy 机器 — R3 ok
|
||||
- [x] C05.2 外壳:标题与标签区、左导航、内容区、右属性区、底部状态区;空侧栏不占位;窗口内转场。已验证(macOS):八应用共用 `ely_ui::Shell`;画廊冒烟与探针(`tests/scripts/shell.sh`)断言左右栏渲染与收起、one→two→one 转场、转场后键盘焦点保留、侧栏与内容区宽度(页面键不再重建内容子树);空栏占位与删栏两种变异实现均触发失败 — R3 ok
|
||||
- [x] C05.3 命令:统一文件菜单命名、Cmd/Ctrl 语义、命令面板(UX-01、UX-02)。已验证(macOS):应用菜单在前使 File 成为独立菜单;菜单标签、菜单到动作的映射、六个命令经快捷键与菜单动作逐条触发且结果一致;面板经真实按键打开、输入查询、Enter 执行;toggle 关闭走面板自身的焦点归还。键位错接、菜单错接、绑定缺失、on_run 置空、派发丢弃五种变异均触发失败 — R5 ok
|
||||
- [x] C05.4 交互规范:焦点与 Esc、禁用原因与混合态、长任务、失败保留输入、危险确认(UX-03 至 UX-07)。已验证(macOS):Tab 经 `tab_stop` 句柄按 nav→content→props→chip 顺序行进,Esc 只关当前浮层;阻塞命令菜单置灰、面板执行给出原因并 toast;混合选择显示 Mixed;长任务工作在后台执行器(范围/状态/错误/取消/重试,取消在检查点生效且错误优先);PromptDialog 校验失败保留输入与原因;ConfirmDialog 命名对象与影响并键盘可达;探针覆盖纯键盘流程,截图逐像素比对固化渲染 — R4 ok
|
||||
- [x] C05.5 国际化与无障碍:简中与英文、非颜色表达、角色与名称、输入法与缩放(UX-08 至 UX-12)。已验证(macOS):语言探测重写(macOS preferredLanguages、Linux 首个非空优先、C/POSIX→en,单测);组件库 7380afb 起面板族文案经 i18n(内建 en/zh-CN)、对话框带角色/名称/说明、正文与校验错误可读、禁用带说明;对话框与面板 Enter 改走 key-down 动作,IME 候选确认不再提前提交,key-up-only 回归锁死且旧实现变异触发崩溃;resize 800×600 往返布局与点击断言;100%/150%/200% 与跨屏缩放需非 Retina 及多屏硬件,本机未验证(UX-10 部分) — R6 ok
|
||||
- [~] C05.6 侧栏可调宽度(用户 2026-10-08 提出:左右侧栏太占地方):八个应用共用的外壳侧栏默认变窄(左 220、右 320,原 240、380),内侧边缘可拖动(左至少 160、右至少 220,至多窗口的 45%),宽度按应用记在其设置里、重启后照旧;`panels.sh` 真窗口核对默认宽、拖宽、记住、重启恢复 — R1 FAIL 三项已修:拖动后立即退出丢了宽度(退出时写入未存的宽度,窗口位置同);存着的无效宽度静默当作默认(严格解析,读不出即在设置窗口写明并用默认);写入失败只进日志(设置窗口以 Banner 写明,下次拖动或退出时重试);`panels.sh` 加三个场景(去掉退出时写入即失败) — R2 FAIL 两项已修:一个“未保存”提示供所有保存共用,通用设置保存成功即清掉侧栏的失败(按保存种类各记各的:通用、侧栏、窗口位置,各自成功才清);侧栏重试成功后旧提示仍在(成功即清其提示);`panels.sh` 加交错保存与修好后重试(全部清除即失败) — R3 FAIL 两项已修(不再送审):启动时记下的读取错误在拖动写入有效宽度后仍显示(读取错误也按种类记,该种类写入成功即一并清掉);测试在设置页内容进 AX 树之前就断言提示已消失(先等页面控件出现);回归:无效宽度经拖动修好后提示消失,去掉清除即失败
|
||||
- [ ] C05.7 窄窗口版面(用户 2026-10-08 提出:缩窄窗口时控件挤成一团、越出边框):外壳一次排两侧栏,内容区至少 360,侧栏先缩后藏(`room`,单测);组件库 6d95b24 至 69265b4 的纵向滚动区、可折行属性行、只显示图标的分段与标签、可缩的状态栏与按钮、折行的提示条、未聚焦文本框显示开头;应用里的固定宽度改为占满且有上限;重复标签的开关只给无障碍名;`tests/scripts/layout.sh` 六个应用三种宽度无元素越出窗口左右并留截图 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
- [ ] C05.8 文档缩放(用户 2026-10-08 问 Doc、Table、Show 为何不能缩放):`ely_office::Zooming`,Cmd+= / Cmd+- / Cmd+0、“视图”菜单、状态栏缩放控件(组件 `ZoomControl` 的属主模式),Cmd+滚轮与捏合以指针为中心,25% 至 400%,ElyShow 回到适合幻灯片;单测 `zooming_keeps_the_pointed_spot_and_walks_the_steps`,真窗口按键记过 125%、150%、125% — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C06 文件、保存与恢复(§6)
|
||||
|
||||
- [x] C06.1 文件会话:位置、指纹、修订号、状态机、同文件聚焦(FILE-01、02、03,§6.1)。已验证(macOS):新建 `crates/ely-files`;新建文件首存前无路径;加载/已保存/未保存/保存中/只读/外部已修改/加载失败/保存失败八种状态;保存票据带任务序号与加载代次,旧修订完成不清新编辑状态,重载使旧票据失效,重复与旧回调不释放他人槽位;同文件(含硬链接、符号链接)再开聚焦或只读视图,原子替换后身份实时重取,路径解析错误全部传播且登记原子;32 测试,Windows/Linux 目标编译过 — R10 ok
|
||||
- [x] C06.2 保存:原子替换、外部修改检查、冲突流、导出独立、路径与错误(FILE-04 至 FILE-10)。已验证(macOS):暂存写入+校验+替换,失败留原文件与未保存内容;统一 `save` 流程先查外部修改(冲突保留两版,重载/保留本地/比较三条出路);替换保留原权限与 macOS ACL;非原子卷先排他备份(禁截断禁跟链、源权限继承、名称无界生成),探测可注入并经跨目录符号链接锁死单行回退;导出按解析后路径与 inode 拒同文件;附件导入只复制且重名候选计数器必保;空格/中文/大小写/满长文件名全过;58 测试,Windows/Linux 目标编译过。FILE-10 的解码域错误(损坏/加密/缺字体/不支持)随各引擎任务落地 — R5 ok
|
||||
- [x] C06.3 恢复副本与崩溃恢复:10 秒、20 点、7 天、容量;内容、光标、历史分开说明;加密文档不落明文(§6.2、§6.3、§18.2)。已验证(macOS):`Recovery` 每点暂存发布、单任务占用、20 点/7 天/容量(超限点提交前拒绝、计量含光标)、`tick` 节律;`RecoveryTask` 后台线程每 10 秒快照、condvar 即时取消、stop/drop 都 join、状态格不被磁盘 I/O 阻塞;内容/光标/历史三恢复能力分开报告;恢复排他落盘不跟链接;`discard` 清恢复点并留标记;加密恢复经 AES-256-GCM(RustCrypto,可交叉编译),密钥由 keyring 入系统凭据库(macOS Keychain/Windows 凭据/Linux Secret Service),OS 级排他锁(flock/LockFileEx)保护初始化,锁目录用 getpwuid_r 账户家目录不受 HOME/XDG 影响,加载错误明示不可用不导出明文;89 测试;Linux 的 Secret Service C 链本机交叉缺 dbus 头文件,未验证;Windows 目标编译过 — R11 ok
|
||||
- [x] C06.4 保存策略:显式保存、可选自动保存、外部变更停止覆盖;Mark 1 秒自动保存。已验证(macOS):`PolicyEngine` 三策略(Explicit 默认显式、AutoSave 可选、MarkAutoSave);去抖用单调 Instant(校时不推迟);修订号跟踪——保存期间继续输入的较新编辑保留自己的 1 秒到期,无路径期编辑只记不调度、首保存得路径后续排;外部变更或兼容风险永久停止自动覆盖并一次性提示;自动保存失败保持未保存状态(下拍重试);通用可选自动保存复用 Mark 的 1 秒去抖(PRD 未定值,记此决策);99 测试 — R2 ok
|
||||
- [~] C06.5 文档应用接入恢复副本与崩溃恢复:Doc、Table、Show、PDF 每 10 秒后台恢复点(加密文档封存),崩溃后以新副本恢复并分说内容、光标、历史;普通未签名文件可在设置里开自动保存(§6.2、§6.3;2026-10-08 发现只有 ElyMark 接了 `ely_files::Recovery`) — 已做:`ely_office::Recovering`(每 10 秒为有改动、已落定的标签写恢复点,进程崩溃后下次启动以未命名副本打开最新点并提示只恢复了内容);`ely_office::Stopping`(引擎进程退出时上方提示条写明原因与最近恢复点时间,“重新打开文档”起新引擎,有改动的标签从最新点、无改动的从文件重开,没有恢复点的列出);`ely_office::Autosaving`(设置“文档”页的开关,本应用自有格式、无风险、状态为已编辑的文件停手 2 秒后保存,失败或外部改动后不再自动写);Cmd+W 关闭标签移入共用命令(原来只有 ElyPDF 绑定,文档应用里 Cmd+W 落进内核,内核按自己的快捷键关掉文档,worker 随即在已释放的文档上崩溃),worker 拒绝内核关闭框架与退出的快捷键(Mod1+W、F4、Q);`recovery.sh`、`autosave.sh` 真窗口核对 — R1 FAIL 四项已修:恢复出的加密文档丢了封存(重开的副本接过原恢复点的槽,封存随之);恢复副本一载入就删了唯一的旧恢复点(改为新标签接过旧的恢复点文件夹,旧点留到新点写下,载入或解封失败的条目留在索引里);加密 PDF 的恢复副本要密码而重开不问(加密的副本照样询问,答后仍是未命名副本);空闲的 ElyPDF 引擎退出无人察觉(每次恢复计时都查引擎是否已退出);`pdf-recovery.sh` 真窗口核对加锁 PDF 的封存点、两次崩溃、空闲引擎退出与重开 — R2 FAIL 三项已修:打不开的恢复点在正常退出后的下一次启动被删(索引里未接过的条目标为保留,干净结束后只删其余的,下次再试);索引写不进只进日志(提示一次,之后每次计时重试,写成即止);接过旧恢复点后“最近的恢复点”仍为“尚无”(接过时取其时间);另:崩溃后的重开只在确有条目时写索引 — R3 FAIL 一项已修并加回归,三轮用尽:恢复索引原地覆写,写到一半失败即坏掉上一份(改为暂存后原子替换,`a_failed_index_write_keeps_the_last_one`)
|
||||
|
||||
## C07 LOK 共用接入(§7)
|
||||
|
||||
- [!] C07.1 LOK 固定构建与加载:自建 26.2.6.3 并打 `patches/libreoffice` 补丁,出图块;隔离用户配置,工作进程,打开、关闭、保存、转换,错误码不当成功,不执行宏(LOK-01、09、10、11)。已验证(macOS):补丁构建加载、未打补丁拒绝(官方 dmg 对照)、严格 JSON 版本握手、打开/保存/转换往返、错误码报出不成功、关闭清理(文档释放、joinThreads、kit 销毁、陈锁与 lu*.tmp 清、失败可见);生产 worker 无宏执行入口(dev argv 门 + debug_assertions,release 带 --dev 也拒绝,实测);6 测试。阻塞(不算完成):审查要求「禁用/放开宏两种配置下同一份自动执行文档出标记对照」,但本后端(LOK Aqua/unipoll)任何配置下文档事件都不派发给 Basic(OnLoad/表单按钮经鼠标键盘、温/冷 profile、两种 URL 形式、官方 AcceleratorKeyChecker.fodt 对照、放开宏变异共 10 轮证伪);属本机无法实测的运行验证,审查者与我们在 Linux/LO 7.4.7 上才见到事件触发 — R10 该项仍 FAIL,证据 tasks/reviews/C07.1-r1..r10.md
|
||||
- [~] C07.2 图块渲染:失效通知、修订防旧覆盖、坐标、缩放、滚动、DPI;GPUI `LokView`(LOK-03、04)。已验证(macOS):编辑与 UNO 删除带精确矩形抬代际,只动光标不抬;视图只丢相交图块,旧代际图块不落;文档尺寸随批次刷新;滚动在增删页、缩放、视口变化后都夹回文档内;逻辑像素布局、DPI 只进纹理(2x 下一块 256 逻辑像素、512 纹理像素);BGRA 原样进 RenderImage;释放视图即停事件轮询;坏几何报错且 worker 存活。查出并修复 `postUnoCommand` 发往桌面活动框架(补丁 0002,补丁前 3/3 失败、后 15/15 通过)。23 测试。未验证:Linux/Windows TDF 构建(无补丁 0002) — R3 FAIL 一项(滚动夹定)已修并加回归,三轮用尽
|
||||
- [~] C07.3 输入:键盘、鼠标、中文输入法组合、剪贴板文本、富文本、图片、表格(LOK-06、07)。已验证(macOS):键盘(vcl 键码与修饰位,文本走输入法)、鼠标(按下/拖动/松开、点击计数、按钮,坐标与选区同一规则)、输入法组合开始/更新/提交/取消经 gpui `ElementInputHandler` 按 UTF-16 处理组合内选区与替换范围;光标、选区、光标可见性经回调画在视图上;系统剪贴板(`ely_platform::clipboard`,arboard)收发纯文本、HTML(富文本、表格)、PNG;剪切先写系统剪贴板再 `.uno:Cut`(Calc 不弹对话框);粘贴先验载荷(PNG 与 HTML 内嵌图片须解码),以撤销栈顶条目识别新步骤(满 100 步仍成立),失败回滚并报错;输入失败经 `LokError` 事件给应用;真窗口探针 `tests/scripts/lokview.sh` 断言输入法、点击、拖动、复制粘贴的文档结果,去掉鼠标处理或输入法变异均失败;49 测试。未验证:系统级拼音输入法实机、Linux/Windows 剪贴板运行(仅目标编译)、粘贴回滚分支无真实内核失败用例 — R3 FAIL 两项(HTML 内嵌坏图、撤销满)已修并加回归,三轮用尽
|
||||
- [~] C07.4 命令与状态:功能到接口映射表,状态回调,字体样式以内核为准,对话框由 Ely 实现,无障碍文本桥(LOK-02、05、08、12,§7 尾段)。已验证(macOS):`ely_lok::commands::COMMANDS` 为映射表(界面 id→UNO 名、带类型参数、确认方式),worker 只跑表内命令;参数按类型校验(字体须在内核字体表,字号 1–999 且 0.05 步,纯十进制);确认方式按实测:完成结果(SelectAll/粗斜体/下划线/删除线/样式)、同步撤销计数(Calc 另认单元格编辑文本变化)、状态回读(字体/字号),失败经 Undo 回滚并报错;状态回调(含持久已知状态)、选中对象框、字体与样式表来自内核;内核对话框(WINDOW created/JSDialog)在回调里即关并经 `LokError` 报出,Calc Backspace 不再卡死;无障碍桥:打开即取当前段落,段落/插入点/选区(-1 为无)、Calc 单元格与输入行文本,`Role::MultilineTextInput` + `aria_value`,真窗口 AX 树读到打开时与编辑后的段落;空闲时 worker 退出经 `LokError` 报出;70 测试。未验证:Linux/Windows 运行、Calc 打开时单元格文本(内核快照为空)、Calc 撤销按单元格编辑文本变化确认(非计数)— R3 FAIL 两项(Calc 单元格编辑撤销、字号容差)已修并加回归,三轮用尽
|
||||
|
||||
## C08 ElyDoc(§8)
|
||||
|
||||
- [~] C08.1 应用壳、新建、模板、打开、保存、格式规则(DOC-01)。已验证(macOS):ElyDoc 文档标签共用一个 LOK worker,启动即开空白文档,焦点在文档,Cmd+N 立即可用(真窗口 AX 探针);新建:空白/报告/信函模板与本地模板文件(复制后无标题打开,原件不动,含宏的模板进入风险模式);打开同一文件聚焦已有标签;保存保留原格式(kernel 以 TakeOwnership 写副本,`ely_files::save` 原子替换),另存为只写 DOCX(默认)或 ODT,转换写新文件(别名指向原文件时按原格式、拒绝转换);外部修改走冲突对话框(从磁盘重载/保留我的/另存副本);含宏文件(docm/dotm、OOXML/ODF/OLE 宏结构、绑定命名空间的嵌入 Basic)进风险模式,保存只能另存副本;状态栏显示已保存/已编辑/正在保存/磁盘已改动/保存失败/风险模式,载入中显示占位标签;未保存关闭与退出询问(等待输入落地,并以内核撤销历史核对);保存等待排队输入;文件面板错误报出;页面居中、页外为主题底色;17 个应用测试、ely-files 104、ely-lok 72。未验证:原生打开/保存面板的自动化(测试直接给路径)、状态栏文字的 AX 暴露(组件缺口)、Linux/Windows、LOK 随包打包;左栏导航与右栏属性分属 C08.6 与 C08.2 — R3 FAIL 三项(关闭确认时序、只读会话失败、XML 宏识别)已修并加回归,三轮用尽
|
||||
- [~] C08.2 文字、段落、样式(DOC-02、03、04)。已验证(macOS,带补丁 0003 的 LOK):右侧属性栏(文字、段落、样式三组)跟随活动标签,选中对象时让位;字体取内核字体表,字号、粗斜体、下划线、删除线、上下标、颜色与高亮(色块弹出色板,“无”清除高亮);对齐、缩进、行距 1/1.5/2、段前与段后分设(磅,内核回显,另一项保持)、项目符号与编号、升降级、制表位(厘米,添加与逐个删除);段落与字符样式的应用、新建(名称校验,列表随即重读)、按所选更新,当前段落样式与字符样式都取内核状态(光标移动、撤销、重新显示后同步);混合选区各控件显示“混合”,不冒用首项,样式不唯一时“更新”禁用并说明原因;段距连续快速编辑不互相覆盖,数值框只在回车、失焦、步进时提交(组件库 `NumberInput::on_commit`,真窗口 `doc-props.sh` 断言两值与两条命令,恢复旧回调即失败);字符样式带来的属性不一致也显示“混合”(补丁 0003 扩到 `SwTextNode::GetParaAttr`);每条命令经 `COMMANDS` 白名单与内核确认,参数错误报出;真窗口点击加粗、红色与段后,文字与控件同步;落盘 DOCX 含颜色、高亮、上标、制表位、列表、新样式及其父样式与更新后的对齐,更新字符样式只改光标处的样式。21 个应用测试、ely-lok 84。未验证:自定义字号(菜单为常用字号加当前值)、内核界面名与程序名不同的内置样式在列表中的选中、Linux/Windows(需带补丁 0002、0003 的 LOK);“标题样式驱动大纲和目录”分属 C08.6 与 C08.4 — R3 FAIL 两项(字符样式属性的混合状态、段距回归未经真控件)已修并加回归,三轮用尽
|
||||
- [~] C08.3 页面、分节、页眉页脚、表格、图片与对象(DOC-05、06、07、08)。已验证(macOS,带补丁 0003–0007 的 LOK):插入菜单(表格对话框、图片经系统文件面板、分页符、分节符(确认后从当前页复制出新页面样式“第 N 节”再分页,前后两节各自分栏与页眉页脚;内核复制页面样式会清空撤销记录,故先确认)、页码、总页数);属性栏页面组(纸张 A3/A4/A5/B5/Letter/Legal 与当前尺寸、纵横向连同尺寸互换、四边页边距厘米连续编辑不互相覆盖、分栏 1–3、本节页面样式、页眉与页脚各自的开关及首页不同与奇偶页不同,未开启时禁用并说明;转向连同尺寸一步撤销);表格组(光标在表内出现:行列插删、合并拆分、平均列宽与行高、适合行高、全部或无边框、底色、标题行每页重复、行跨页允许或保持整行、删除表格,内核禁用的操作灰显并说明);选中图片时只显示图片组(宽高厘米保持比例、裁剪、替换、四种锚点、六种环绕、上下层次);视图画出选中对象的框与 8 个手柄,拖动手柄经 `setGraphicSelection` 缩放,裁剪模式下裁剪(真窗口实测);落盘含页面尺寸与方向、边距、分栏、页眉页脚与首页/偶数页、分节页面样式、页码与总页数、表格与表头重复、边框底色、行保持、合并单元格、图片锚点与环绕、裁剪、层次。组件库 `NumberInput::on_commit`、`ToggleButton::disabled`。26 个应用测试、ely-lok 94;修复 worker 请求在内核空闲任务中途重入的崩溃(补丁 0007,崩溃栈由 worker 信号处理打印)。未验证:Linux/Windows(需补丁 0002–0007 的 LOK)、表格行高列宽的数值设定(以平均与适合代替)、对象的精确位置、JSDialog 崩溃修复的稳定复现、C07.4 的无障碍选区测试约十余次全量运行失败一次(内核未发事件,原因未明) — R3 FAIL 一项(页面回归测试只等页面项、未等尺寸的竞态)已修,两测试改等完整页面状态,串行全量连跑三次 26/26,三轮用尽
|
||||
- [~] C08.4 引用结构、公式、审阅(DOC-09、10、11)。已验证(macOS,带补丁 0003–0008 的 LOK):插入菜单加目录(默认目录,标题随界面语言)、更新目录、脚注、尾注、书签(名称不可空、不可重名)、超链接(无文字时以地址为文字)、交叉引用(指向书签,显示文字、页码、章节号或上下方;无书签时说明)、题注(选中图片或公式为“图”,表内为“表”,编号与“: ”分隔照内核题注对话框;无对象时说明)、公式(文字转内核公式对象;选中公式时预填原式并整体替换,一步撤销)、批注;审阅菜单加批注、批注与修订面板、修订开关、全部接受、全部拒绝。右栏可切到审阅面板:修订开关随内核状态(未知时禁用)、全部接受或拒绝,批注按线程列出(作者、时间、正文),选中线程即滚到批注处并放置光标、可回复;修订逐条列出,可逐条接受或拒绝,点条目定位。列表随内核批注与修订回调刷新。补丁 0008 让目录、题注、公式不经内核对话框。真窗口实测:批注、回复、修订开关、键入被标记、逐条接受、公式插入与渲染。落盘含目录、脚注、尾注、书签、书签引用、超链接、题注序列、公式对象与原式、批注与回复、接受后的文字、拒绝后删去的文字。31 个应用测试、ely-lok 78;公式含补充平面与组合字符时不吞前文,替换选中公式留在原处并保留锚点、间距与对齐(浮动与行内),一步撤销复原;批注正文保留首尾与中间空段落未验证:Linux/Windows(需补丁 0002–0008 的 LOK)、作者名(内核显示“Unknown Author”,作者设置待定)、批注组件内部文案(Open、Resolved、Reply、相对时间)为英文(组件 i18n 上游待办)、交叉引用只指向书签(不含标题与题注)、公式导出 DOCX 后的可编辑性、插入目录后光标落在目录内(内核原样) — R3 FAIL 一项(替换浮动公式时把手动纵坐标当作基线自动定位清掉)已修:只对行内公式清除自动纵向定位,回归断言页锚点公式的 svg:x、svg:y 不变,三轮用尽
|
||||
- [~] C08.5 查找替换、拼写、字数、文档比较、邮件合并(DOC-12、13、14)。已验证(macOS,带补丁 0003–0009 与英文词典的 LOK):工具菜单“查找和替换”(Cmd+F)在页面右上打开查找框:键入即计数并高亮全部,“n/共 m”随上下一处走,区分大小写、全词、正则(内核 ICU)三个开关,替换一处后重新计数,无当前项时“替换”先选下一处,全部替换;查询可为空白;上下一处滚到匹配处;查找后文档被改动时计数标“已改动”,回车或下一次查找重新计数(边打字边计数会选中全部匹配,故不自动);比较、预览、生成都先等源标签排队的输入落到内核,且始终用发起时的标签;计数中按回车或下一处会等计数落地再定位;字段值里的 `«列名»` 原样保留(先换成一次性记号再填值);生成先写暂存文件再以独占新建发布,检查之后才出现的同名文件不被覆盖;字数取内核(补丁 0009),状态栏显示全文或选区字数;拼写用随包 Hunspell 英文词典,错词红色波浪线由内核画,右键在插入点下给建议与“全部忽略”(组件库 `ContextMenu::manual`,967d1e4,注释在 aaaf0d5);审阅菜单“与其他版本比较…”把当前文档作副本开新标签并标出对较旧文件的修订,两份源文件不动,审阅面板随即打开;工具菜单“邮件合并…”选 UTF-8 CSV(首行为列名,空名、重名、无数据行、列数不齐、列名含 « 或 »、非 UTF-8 均报错),字段按钮在光标处写入 `«列名»`,按记录号预览为新标签,生成按 DOCX 或 PDF 每行一个文件写到所选文件夹,任一目标已存在则整体拒绝,不发邮件。真窗口实测:查找计数、下一处、替换一处、拼写右键建议与应用、合并面板、插入字段、预览。40 个应用测试、ely-lok 81。未验证:Linux/Windows(需补丁 0002–0009 的 LOK 与词典)、写错的正则只显示“无结果”(内核不报原因)、查找框与批注面板的组件内部文案为英文(组件 i18n 上游待办)、英文以外的拼写词典(只随包英文,待用户确认语种)、`«列名»` 字段写法待用户确认 — R3 FAIL 三项已修:连续两次生成各自占用独立模板与暂存目录;新查询或清空查询使在途结果作废;计数中的多次定位按序排队执行;各加回归,三轮用尽
|
||||
- [~] C08.6 打印、导出、长文档导航、兼容风险提示、往返测试(DOC-15、16,§8.3,QA-10)。已验证(macOS,带补丁 0003–0011 的 LOK):文件菜单“导出为 PDF…”(Cmd+E)与“打印…”(Cmd+P)先开同一对话框:页面全部或范围(起止页受页数约束)、批注印在页边、标出修订;导出经系统存储面板写 PDF,完成后提示,目标是打开着的文档(符号链接或硬链接也算)即拒绝;打印把同一 PDF 交给系统打印面板(PDFKit),取消或打印都记日志;加密文档询问密码(遮蔽输入),错的密码再问,对的打开;左栏导航(工具菜单与 Cmd+[ 返回)分大纲、页面、书签三页,大纲、当前标题与书签位置取内核(补丁 0010;当前标题按文档顺序,分栏也对),当前页按光标所在页,点标题、页、书签即按位置跳转,跳前记下所在处(上一跳未落下或光标未跟上时以其目标为准,落下后按实际光标),“返回”逐级回原处(关闭的标签提示);缺字体时页面上方提示条,文档写的字体列表全缺照报,有成员可用的列表不报(补丁 0011)。往返:合同(编号列表、批注、修订)与长报告(标题、目录、跨页且表头重复的 80 行表格)经 DOCX 往返,结构与文字保留。真窗口实测:导航、当前项随跳转(含同页两标题)、返回、缺字体提示、导出对话框与范围、系统打印面板的三页预览与取消。43 个应用测试、ely-lok 110。未验证:Linux/Windows(需补丁 0002–0011 的 LOK;打印在其他平台报错未实现)、QA-10 与 Microsoft Office 互开(本机无 Office)、真实打印机出纸 — R1 FAIL 四项已修:导出可覆盖打开着的源文件;连续跳转时“返回”记成旧光标;文档写的字体列表被整串滤掉;同页标题的当前项不随跳转;各加回归 — R2 FAIL 两项已修:导出与另存为按文件身份比对(硬链接也认出),另存为到本文件的硬链接按本文件格式判定转换;上一跳落下后不再当作在途;各加回归 — R3 FAIL 一项(分栏时当前标题只比纵坐标,落到右栏标题)已修:当前标题改由内核按文档顺序给出(补丁 0010 加 current),加两栏回归,三轮用尽
|
||||
|
||||
## C09 ElyTable(§9)
|
||||
|
||||
- [~] C09.1 应用壳、工作簿、区域编辑(TABLE-01、02)。已验证(macOS,带补丁 0003–0013 的 LOK):ElyDoc 的文档外壳移入 `crates/ely-office` 后两应用共用(ElyDoc 41+2 个测试照过,真窗口脚本 doc-shell、doc-props 照过);ElyTable 启动即开空白工作簿,名称框与公式栏随单元格(回车跳转或写入,Esc 交回网格),行列表头按内核几何对齐图块(实测拖宽 A 列后表头随之重排),点表头选整行整列,拖表头边改行高列宽,填充柄拖到 A6 续出等差序列,选区统计取内核数值、名称换成界面语言(平均值 21 求和 42);工作表标签切换、加号插入、右键菜单与“工作表”菜单的插入、重命名(名称校验)、复制、左右移、隐藏、显示、删除(列出引用该表的公式数、第一个公式格与命名区域数);“单元格”菜单插删行列与单元格、合并拆分(两个以上数据格先问移入或保留隐藏)、行高列宽(厘米,作用于整个选区)与最适、隐藏显示行列、冻结(真窗口 B2 冻结后上下左右滚动,首行首列不动,表头同步);Tab 与 Shift-Tab 在格间移动;名称框跳到远处时画布随单元格光标滚动;右键工作表标签先选中该表再出菜单;表头可 Tab 到达、带列头/行头角色与名称、回车或空格选整列整行;新建工作簿另存 XLSX 后重开工作表齐全。内核测试 ely-lok 119(两工作簿各自切表与写入、工作簿与区域编辑各一组、冻结状态、INDIRECT 计数、作者署名),应用测试 10(删除与合并的确认绑定发起的工作簿、含切表前后输入顺序、切表等待期间换工作簿、名称框随活动工作簿);组件库加 `Tabs::on_context`(2f02c82)。未验证:Linux/Windows(需补丁 0002–0012 的 LOK)、`ely_platform::user` 的 Linux、Windows 分支未经编译器(本机无交叉工具链)、行列分组与筛选隐藏的表头、触控板以外的大表滚动性能(C09.5 的 TABLE-16)、`a11y_follows_the_focused_paragraph` 间歇(单跑 8 败 1,全量 4 败 2,原因未明) — R1 FAIL 九项已修:首帧工作表标签崩溃;切表越过输入队列;单元格光标不带动滚动;冻结不分格;尺寸对话框只改一行一列;右键菜单丢失被点的表;INDIRECT 引用未计入删除说明;平面 ODF 宏检测漏子元素命名空间(改用 quick-xml 按命名空间作用域解析);表头无角色与键盘操作;各加回归 — R2 FAIL 五项已修:多工作簿时 worker 未切到文档自己的内核视图(现每次请求先 setView);切表未进有序输入队列;延迟切表丢失发起的工作簿;远处跳转被缓存的文档尺寸截住(真窗口 A100 与连续下移到 A130 实测可见);切换工作簿后名称框与公式栏不刷新;各加回归 — R3 FAIL 两项已修,三轮用尽:删除工作表与合并的等待回调重读活动工作簿(现请求与确认框带发起标签的 id,经 `run_in` 执行;回归在去掉修复时失败);水平滚动只比左边缘(现比右边缘,回归在去掉修复时失败;真窗口名称框跳 M1,整格与填充柄可见)
|
||||
- [~] C09.2 公式输入、计算、计算控制、名称与引用(TABLE-03、04、05、06)。已验证(macOS,带补丁 0003–0015 的 LOK):公式为 Excel A1 语法与逗号分隔(用户定,安装层 ely.xcd;`=SUM(1,2)`、跨表 `'S'!D1`、绝对与混合引用实测);单元格内键入时函数列表在格下(真窗口:`=SU` 列出 SUM 等与签名、说明,下键选 SUMIF、Tab 补全为 `=SUMIF()`),参数提示取内核用法文字,点选单元格插入引用并画框(真窗口点 C3 得 `=SUMIF(C3)` 与品红框),点选后继续键入、F2 再编辑已有公式两引用各画框、提交与取消都清框;语法错误的公式照原样输入不弹询问;状态栏按界面语言解释当前格错误(25 条,含循环引用 Err:522);算术、逻辑、条件统计、文本、日期、查找、动态数组(FILTER、SORT、UNIQUE、SEQUENCE、XLOOKUP)取内核结果;自动计算关闭后改值不重算、重新计算后更新,ods、xlsx、xls、fods 存后重开仍为手动(补丁前 xlsx、xls 变回自动);名称:工作簿与工作表范围定义、改引用、撤销、删除,像单元格地址或含符号的名称由内核判定后拒绝,名称框跳转名称,删去目标行后名称标为失效;“公式”菜单(自动计算、重算、名称、追踪错误与引用从属、移去箭头)。同轮修内核:JSDialog 发送者在发送中被关闭的释放后使用(0005 增补)、Aqua 直接超时丢失唤醒致新载入 XLSX 无初始状态(0015,约四分之三复现到 5/5 正常)、载入选项未被 LOK 解析。测试:ely-lok 公式 7 项、ely-table 17 项(含定义名称取所选区域) — R1 FAIL 五项已修:名称列表“转到”按名称跳转,跨表的工作表名称失败、同名工作簿名称被当前表遮蔽(现由内核列出每个名称的绝对区域,转到即跳该区域;非区域名称不可转到);定义名称在排队输入落地前读选区(现等发起工作簿的输入落地,并经新加的 `.uno:SelectedRange` 同步向内核要所选区域,选区显示为名称时也得到其区域,多个区域时提示后留空);等待后的名称列表改用当前工作簿(现带发起的工作簿 id);七处注释超长。各加回归(跨表名称与遮蔽、排队后定义与名称选区、等待中换工作簿),转到的回归在旧实现下失败 — R2 FAIL 一项已修:定义名称的“范围”列表取自当前工作簿,等待中换工作簿后列出别的工作簿的表(现取自对话框所属工作簿);同类问题一并收口:重命名、显示工作表、行高列宽对话框都带发起的工作簿 id,确认时只作用于它。回归:等待中换工作簿后范围列表与定义落点、重命名中换工作簿,均在旧实现下失败 — R3 FAIL 一项已修,三轮用尽:名称列表“转到”只移动发起工作簿的选区而不切到它(现先切到该工作簿再跳转,名称框随之显示目标);回归断言跳转后活动工作簿与名称框,在旧实现下失败
|
||||
- [~] C09.2a 内容区随系统深浅色,重绘不闪(用户 2026-10-04 提出;LokView、ElyDoc、ElyTable)。已验证(macOS):内核以 Ely 的深浅两套颜色方案绘制,Writer 页面与 Calc 网格深色时平均亮度约 26–35、浅色约 240–254,切回浅色复原(`the_page_follows_a_dark_theme_and_back`);深色下导出 PDF 仍白底(Quick Look 渲染平均约 255);真窗口系统深色下 ElyDoc 页面深底浅字(含中文)、ElyTable 深色网格,`ELY_APPEARANCE=light` 下为白底;主题切换不把文档标为已改;作废的图块继续显示到新图到达,仍按作废请求重绘(视图测试) — R1 FAIL 两项已修:图块重绘失败只记日志,界面一直显示旧图且同一代际不再重试(现报给应用的错误通路,可视区移动或下次作废时重试;回归注入失败断言报错与移动后重试);主题回归绕过视图(现经 `LokView::follow_theme` 与有序队列驱动,断言内核实际绘出的深浅;把深色方案名改成浅色时该测试失败) — R2 FAIL 一项已修:单元测试无法初始化应用主题,`render` 取主题这一步无回归(把它固定为浅色时单测全过);新加真窗口脚本 `tests/scripts/theme.sh`,以 `ELY_APPEARANCE=dark/light` 启动 ElyTable、截窗口量网格亮度(实测深 26、浅 252),上述变异下脚本报“the dark grid paints at 252” — R3 FAIL 两项已修,三轮用尽:脚本只量平均亮度,不画图块时底色也能过(现另量网格线像素占比,只有内核图块画网格线;去掉图块绘制的变异下报“no grid lines (0 per mille)”);缺屏幕录制权限时 SKIP 被当亮度比较而报 FAIL(现判断在顶层,权限拒绝时 SKIP 退出 0,已模拟验证)
|
||||
- [~] C09.3 格式、数据整理、规则(TABLE-07、08、09) — R1 FAIL 九项已修:分列、数据验证、条件格式列表的等待回调重读活动工作簿(现带发起的工作簿 id);粘贴为公式跨工作簿时按纯文本粘贴丢公式、粘贴为值会执行 `=` 开头的文字(现只用本视图复制的内核单元格,其余明确拒绝并提示);单元格规则缓存只按地址,原地设规则或清除后不刷新、同地址切表复用(现按工作表计数、地址、编辑数);清除数据验证发空的运算符与样式被内核拒绝(现发完整的“任何值”);“任何值”带输入提示时被当作无规则丢掉提示(现以有无运算符区分);格式面板同地址切表不重读(同上,键含工作表计数);日志记下文档自己的验证标题与内容(现只记种类,内容只进提示);边框验收只看命令成功(现核对存档单元格属性,全部线关的变异下失败);补丁超 500 行(拆为 0016–0018)。各加回归(ely-table 23 项,含三条等待路径、原地设规则与清除、同地址切表;ely-lok rules 7 项)。同轮按用户要求修卡顿,见 agents.md 2026-10-05 决策 — R2 FAIL 五项已修:粘贴为值/公式只比文字,另一工作簿同样文字的复制可通过(现以 Ely 复制写入 HTML 的唯一标记识别,经真实剪贴板验证);数据验证、条件格式列表、数据区、筛选按钮、序列、格式代码、名称、删除工作表、合并等仍在界面线程读内核(现全部经后台读取;保存导出、导出 PDF、比较、邮件合并同改,保存期间状态为正在保存,同标签的保存排队);切换文档时审阅与导航仍显示上一文档的列表(现切换即清空);读取失败后立即重读成环(现记下已问的状态,等下一次变化);按键到新图块的计时可被旧图块结束(现只在编辑落地后的新代际图块到达时结束,回归覆盖)。另修异步后暴露的两处:同一标签的两次保存并发(现排队)、替换一处后的过期标记晚到使“全部替换”只重新计数(现计数结果即刻清除过期并作为基准) — R3 FAIL 五项已修,三轮用尽:导出期间又有编辑时“保存后关闭”仍关闭并丢失这些编辑(现回到关闭询问;回归 `typing_during_a_closing_save_is_never_lost`);格式代码校验返回后发给当时的活动工作簿(现发给发起的工作簿;回归 `a_format_code_lands_in_the_workbook_that_asked`);两个标签同时另存为同一路径时后者覆盖前者(现写入前按文件身份再查持有者;回归 `two_tabs_saving_as_one_path_keep_one_owner`);邮件合并预览在模板导出完成前未保留标签号,交错的预览串用模板(现发起时保留;回归 `interleaved_previews_keep_their_sources`);关闭干净的标签仍在界面线程读撤销历史(现后台读;回归 `closing_a_clean_tab_never_waits_for_the_worker`,worker 被占 400 毫秒时关闭调用不足 100 毫秒)。同轮查出事件等待阻塞后台线程(每个文档一个,最多一秒)的根因并改为异步唤醒,测试由约 80 秒降到约 15 秒
|
||||
- [~] C09.4 透视表与图表(TABLE-10、11) — 补丁 0019(透视表)、0020(图表);ElyTable“插入”菜单:数据透视表(新建/原地修改,字段放行、列、筛选、值,值函数求和、计数、平均、最大、最小,对话框显示源区域)、刷新数据透视表、图表(柱、条、线、散点、饼;区域、标题、图例、X/Y 轴标题;选中图表时为修改,一步撤销)。顺带修根因:无数据时 `select.data` 留下单格标记使回车不下移;组件库 e21336f 让属主直接移除的对话框(含一起移除的嵌套浮层)交回焦点。R1 FAIL 六项已修:图表区域解析不了即拒绝;对话框的读取回来时发起的工作簿已非当前标签即丢弃(`Tabs::ask_in`,ElyTable 全部对话框与合并询问);透视表数据布局字段读写保留(空名),读出原样写回不改表;嵌套浮层一起移除时焦点回到最外层之前;补丁拆为 0019、0020;注释压到十字内。R2 FAIL 三项已修:图表区域另查所指工作表存在(`sctools::chartRanges`,插入与修改共用;原样“bad range”修改使 worker 因未捕获异常退出,现拒绝且撤销步数不变);已在表中的字段写回时保留小计函数(含小计的 FODS 往返测试,去掉修复时失败);对话框控件经组件库 8658999 的 `Select::label`、`TextInput::label` 与开关自身的 `label` 命名(真窗口 AX 树英文与中文核对:图表对话框六项、透视表各字段、公式栏两项与格式面板两项原先也无名,一并修)。R3 FAIL 一项已修,三轮用尽:图表修改的撤销原只存外观 JSON,换类型后撤销丢掉堆积等设置;现撤销存整个图示与主标题的副本,外观先改、区域后改,撤销先回区域再回图示(`undoing_a_new_kind_brings_the_whole_chart_back`:堆积柱形图改饼图后撤销,导出仍带 `chart:stacked="true"`;换回旧做法时失败)。测试:ely-lok `client::tests::analysis`、`enter_and_tab_move_the_cell_cursor`,ElyTable `tests::analysis`,组件库 `a_dialog_its_owner_drops_hands_focus_back`、`nested_dialogs_dropped_together_hand_focus_back`,ely-lok `a_read_layout_applies_back_unchanged`,ElyTable `a_dialog_asked_from_a_workbook_left_behind_is_dropped`;真窗口看过两个对话框与插入的图表。
|
||||
- [~] C09.5 CSV/TSV、导出、打印、保护、大数据浏览(TABLE-12、13、14、15、16,§9.3,QA-11)。已验证(macOS,带补丁 0003–0022 的 LOK):打开 csv/tsv/tab/txt 先出导入对话框(编码、分隔符、引号、逐列类型,预览按所选读),GB18030 与 UTF-8 样本、引号内含分隔符、前导零与 20 位编号保持文本、`=1+1` 文字不执行、年月日列读成日期;导出 PDF/CSV/TSV(文本只写当前表的显示值,对话框写明损失),打印经同一 PDF 交系统面板;页面设置(打印区域、重复行列、方向、三种缩放)与行列分页符,XLSX 往返保留区域、标题行列、横向与按页缩放;工作表保护(密码、八项许可)、锁定与解锁单元格,锁定格不可改、错误密码仍受保护;XLSX 载入总是重算,未知函数显示 #NAME? 且公式保留,过期缓存不冒充结果;外部链接显示保存值并在提示条列出来源;10 万行表滚到底只留屏边图块(去掉淘汰时该测试失败)。真窗口(macOS,中文界面,深色)看过导出(切到 CSV 时的损失说明)、页面设置、保护工作表三个对话框。未验证:1904 日期系统、Linux 与 Windows — R1 FAIL 六项已修:`.txt` 按扩展名落进 Writer(载入选项加 `FilterName`,补丁 0022;worker 查文档类型,去掉过滤器名时回归失败);列类型只看前 64 KiB(后台读整个文件,18,000 行后的 `007` 仍为文本,存 XLSX 重开照旧);解码静默替换(严格解码,读不通即提示且不能打开);预览行宽不含全部列(行宽按列数,真窗口横向滚到后面的列);预览文字未取主题色(取前景色,真窗口深色可读);缩放模式与数值框挤在一列(拆成两行,真窗口三种模式与宽高两框都在可见区)。分隔符改为下拉(分段控件在中文下被裁) — R2 FAIL 四项已修:提交时重新复制源文件,校验与导入不是同一份字节(导入改为写入检查过的字节,回归在对话框期间改写源文件);旧对话框的扫描回调落进新对话框(按字节快照身份与扫描序号核对,两种先后顺序都测,去掉身份核对时失败);第 65 列起无类型选择(去掉列数上限,真窗口 AX 树有第 65 列);链接只在首次读取(随编辑计数重读,回归在空白工作簿里新加外部引用) — R3 FAIL 三项已修,未经复审:预览拆字段与内核不一致(引号前空格、空行;改按 Calc `impex.cxx` 的规则拆,去掉 csv 依赖;应用回归逐格比对预览与内核导入结果);负号开头的长编号与前导零未判为文本(按数字主体判定,回归中去掉负号处理时失败);补丁 0021 把透视表函数的注释留在新函数上方(移回原处)
|
||||
|
||||
## C10 ElyShow(§10)
|
||||
|
||||
- [~] C10.1 应用壳、幻灯片管理、模板与母版(SHOW-01、02)。已验证(macOS,带补丁 0003–0026 的 LOK):左侧缩略图(深色随主题,隐藏页半透明带标记,点选与右键)、中间按视口缩放居中的画布、右侧版式(12 种)、母版、配色(内核 7 套)、尺寸(预设与厘米);幻灯片菜单与右键:新建、复制、删除、隐藏、显示、上下移、移到开头末尾,各为一步撤销;新建对话框:内核空白演示与随带模板;编辑母版:缩略图与画布换成母版,新建母版,粘贴到母版的文字出现在用它的幻灯片上并存入母版;关闭母版回到幻灯片。真窗口看过新建、母版进出、新增幻灯片。[!] 章节:内核无章节(PPTX 导入丢弃 `p14:sectionLst`,ODF 无此结构),用户定为外部阻塞 — R1 FAIL 七项已修:尺寸框在回显前按旧值重建(发出的尺寸保留到读回,连续改宽高以它为底,回归先后两次改尺寸);尺寸改动与撤销不通知 LOK,视图尺寸停在旧值(补丁 0023 在 `SetPageSizeAndBorder` 与页面格式撤销重做里通知,视图回归读到新尺寸);适配缩放下限 25%(只留上限,3 米页面回归);撤销绕过缩略图重画(读取时比对尺寸、配色、母版表,变了就全部重画;撤销回归逐页比对内核新画像素,去掉比对时失败);缩略图失败后立即重试成环(失败页等下次读取,回归在内核丢文档后只失败一次);缩略图行无 AX 角色(列表与列表项,当前、序号、总数、隐藏说明,真窗口 AX 树核对);宽高框无名称(组件库 7186179 加 `NumberInput::label`,真窗口 AXTitle 为宽(厘米)、高(厘米)) — R2 FAIL 四项已修:同一尺寸重复提交(按显示尺寸去重,回归回车加失焦只一步撤销);整页适配下光标扩大滚动范围、页面跳到左上(适配时范围取文档尺寸,视图回归把滚动范围扩到三倍后页框不动);母版内容撤销不重画引用页(读取时比对撤销与重做步数,撤销后全部重画,回归逐页比像素);模板行无 AX 与键盘(组件库 80c46b1:模板行为列表项,可 Tab 与方向键选取,真窗口 AX 核对)。另修真窗口发现的文字编辑图块全黑(补丁 0024,内核回归撤补丁即失败)与离开编辑后缩略图留编辑框(`LokView::caret`,回归去掉即失败) — R3 FAIL 一项已修:撤销后立即编辑时重做数不变,母版内容的撤销漏画(改按撤销、重做列表的条目判定,回归“撤销后立即隐藏”与“重做后立即隐藏”,改回按数即失败)。另修压测发现的偶发全黑图块与命令丢失(补丁 0025、0026,八路并行各 96 次全绿)。三轮审查已满
|
||||
- [~] C10.2 文字、图形、图片、对象操作(SHOW-03、04、05、06)。已验证(macOS,带补丁 0003–0027 的 LOK):插入文本框(键入)、矩形、椭圆、三角形、直线、箭头、连接线、图片(落在幻灯片中央,替换原位且一步撤销,裁剪进入裁剪模式);对象面板改位置(3 厘米落在 3 厘米)、大小、旋转、锁定、填充与无填充、透明度、阴影、轮廓、线宽、字体、字号、加粗、项目符号、自动适配三档;两对象左对齐、组合、取消组合;参考线添加、移动、删除与吸附;对象列表在 AX 树里是列表,行名为种类、说明为占位符角色;缩略图随面板命令重画。内核回归:读取、按层次选中、twips 换算、参考线、图片居中与替换。应用回归:七项(含回车加失焦只发一次,去掉判定即失败)。组件库 7df3045:`SelectableList` 与 `ListItem` 的 AX 语义。连接线粘附到图形未单独验证 — R1 FAIL 六项已修:清除按钮把 0 当黑色发(清除各发自己的命令,回归走面板常量);已发记录不随读取失效(改按标签与读取次数,撤销后再发与两个同框对象各发各的回归);线类对象无轮廓控件(直线、箭头、连接线、图片显示轮廓与线宽,回归改直线颜色与线宽);列表清空不取消选中(补丁 0027 加只带 `Add` 即全不选,内核与应用回归);混合值显示为 0 且挡住设 0(混合为空、显示“混合”,回归两对象统一设 0 为一步);补丁重叠使重跑构建失败(`scripts/apply-lo-patches.sh` 按序列定位,`tests/scripts/lo-patches.sh` 从第 13 个接着打、再打为空操作,完整构建脚本重跑通过) — R2 FAIL 三项已修:混合数值框里是真的 0,直接回车即发送(组件库 af54877 加 `NumberInput::blank`,空着不提交,组件回归去掉判定即失败);混合选区的自动适配选“不适配”不发命令(补丁 0027 让 `.uno:TextAutoFitToSize` 收 `Fit`,对全部选中对象一步撤销,回归标题缩小、内容增长、合选后不适配为一步);`object.rs` 502 行(文字组移入 `text.rs`) — R3 FAIL 两项已修:“不适配”留着横向自动增长(三档都关横向增长);缩小后撤销改了原段落格式(撤销记录连同文字保存);内核回归导入横向增长文本框设不适配后保存无横向增长、20 磅小框缩小再撤销后绘制与保存的段落不变,去掉保存文字即失败。三轮审查已满
|
||||
- [~] C10.3 表格与图表、备注与审阅(SHOW-07、08)。已验证(macOS,带补丁 0003–0032 的 LOK):插入表格(4×3 落在中央,选中),表格组在光标进格后可用,下方插入行;插入图表(对话框的外观与数据表,非数字格拒绝并指明,选中图表再开为修改,改值一步撤销,读回为新值);备注栏键入后提交为一步撤销,撤销复原;PPTX 备注(每段带全字符格式)可编辑且沿用原格式,格式不一致的备注只读、命令拒绝;批注对话框打开即可键入,回车保存,批注栏中文、显示作者与“刚刚”,选中点亮画布标记,编辑、删除;批注不进对象列表,对象序号也不数批注。R1 FAIL 十项已修,R2 FAIL 三项已修,均带回归(见下)。R1 修后:备注随所属页提交(切页排在提交前也不错页),段落格式一致时沿用、不一致时只读;缺失的点读写为空;类型错的图表 JSON 拒绝;读图表回来时已换标签即丢弃。R2 修后:图表 JSON 经 boost.json 保留类型;散点图 X 按数字写入并导出,PPTX 存后重开可改数据,再存再开值不变;读图表以读取时内核的选中对象核对(同外框的另一图表、跨页都丢弃);表格对话框键入的行列数即生效(真窗口未复验:本机当时有人在用);批注列键盘可达。内核回归:图表插入、修改与撤销,备注读写与撤销,批注序号、作者、编辑、删除,表格插入与增行。 R3 FAIL 三项已修,带回归与变异检验,未复审:散点图改标题删掉原有连接线(只在图表变成散点图时设为只画点,`a_retitled_scatter_keeps_its_lines`);散点图的 X 是文字时应用不拦、内核拒绝后草稿丢失(应用按类型先查 X,指明哪一格,对话框保留,`a_scatter_with_words_for_x_keeps_its_draft_and_names_the_cell`);选中的批注离开所在页后不能再点回(只在所在页显示为选中,`a_comment_picked_on_another_slide_shows_its_slide_again`)。
|
||||
- [~] C10.4 动画、切换、媒体(SHOW-09、10)。已验证(macOS,带补丁 0003–0035 的 LOK):切换按预设设定、读回、一步撤销,Ely 给的十种都能设,全部幻灯片一次设定,类型不合拒绝;动画加入、改开始方式与时长延迟、前后移、删除各一步撤销,十四种都能加,效果的目标序号与放映图层散列相符;图层在 macOS 上有像素(补丁 0033 前全空);真窗口看过淡入预览(标题由暗渐亮,空占位符不显示)与向左推入预览;面板与菜单中文。媒体:嵌入插入在中央、存后重开仍可播放副本,链接文件删去即标为缺失,缺失文件插入被拒;系统播放器(macOS AVFoundation)打开 H.264/AAC 样本、给出 1 秒与 160×90、播放出帧,坏文件给出原因(主线程集成测试)。R1 修后:播放器接口在 Linux、Windows 目标可编译(`tests/scripts/media-targets.sh`,不可用的播放器为空类型),播放实例按标签、幻灯片名与媒体 URL 认定,换页、删除或替换即停;播完再按播放从头开始(平台测试覆盖);向左揭开时旧页在上;预览按标签、幻灯片名与撤销列表认定,读取回来或绘制时不符即弃,停止使在途读取作废(应用测试与变异检验);时长、等待、延迟的回车加失焦只发一次(应用测试与变异检验)。R2 修后:预览另按视图已落地与待落地的编辑认定,键入未成撤销条目也弃(`typing_ends_a_preview_before_any_undo_step`);切换各字段以该页已发未读回的切换为底合并,读回相符或换页即清(`two_fields_sent_before_a_read_both_land`);正在播放的媒体在每次文档状态通知时核对,关闭最后一个标签即停(主线程真窗口探针验证,去掉观察即仍在播;系统播放器只能在主线程打开,应用单测在工作线程,无法常驻测试);`media-targets.sh` 调用应用用到的全部播放器方法(删去 `pause` 即失败);切换合成移入 `preview/compose.rs`,`tests/compose.rs` 以 Metal 画半程帧取像素(向左揭开、向左覆盖、向左推入左旧右新;叠放顺序任一写死都失败)。R3 FAIL 两项已修,带回归与变异检验,未复审:已发未读回的切换按其命令的输入序号认定,命令落地后开始的读取一到即清(不再比值),发出后立即撤销也不会把撤销掉的时长再写回(`an_undone_duration_is_not_sent_again`);动画秒数的去重按幻灯片、效果序号与图层散列区分,回读前给两个效果提交同一值都生效,回车加失焦仍各一步撤销(`two_effects_take_the_same_value_before_a_read`)。未验证:真窗口里插入与播放媒体(本机当时有人在用);Windows 与 Linux 播放器未构建;ElyShow 自身未在非 macOS 目标编译(本机缺 ring 的交叉 C 工具链)。
|
||||
- [~] C10.5 放映与多屏、演讲者视图(SHOW-11、12)。已验证(macOS,带补丁 0003–0036 的 LOK):`.uno:SlideShow` 读出每张的隐藏、备注、切换、动画与媒体(内核测试);母版页脚与页码由内核画成文字层,落在占位处的层次上,字段关闭的幻灯片不画(内核测试,按像素行);放映从头开始与从当前页开始(当前页隐藏也从它开始),单击组逐步播放,跳过隐藏页到黑色结束屏再退出,后退为已完成状态,数字加回车跳页,黑屏、激光笔、Esc 退出,应用快捷键不被吞(应用测试,按键经放映窗口送入,变异检验:按键挂在焦点之下或同步关窗即失败);单击媒体对象按外框命中(应用测试);真窗口:F5 全屏放映、B 黑屏、右键到结束、Esc 结束(日志)。R1 FAIL 九项已修,带回归与变异检验:两窗口的根作用域各自取得焦点(演讲者视图与观众窗口都收按键);连按两次开始只开一次(启动中的状态,每次的副本文件名不同,旧放映结束不清新放映);“从当前幻灯片开始”等已发输入落地,并在同一次 worker 调用里读当前页;放映播的是开始时另存并载入的副本(编辑不进入放映,结束时关闭并删除,副本按应用深浅色设方案);单击组从此前全部动画与入页切换结束之后开始(`steps::click_at`);离开的页按离开时的动画状态画在切换里(完成的退出不再出现);迟到的图层若已不是当前或下一页即丢弃,同时最多读两页;跳转(含跳到本页、Home、End)无切换并重新开始本页;媒体载入失败与播放中失败都停下并在演讲者视图(单屏时在观众屏)写明原因,放映继续。R2 FAIL 六项已修,带回归与变异检验:后退显示本页全部动画已完成(含只有同时、之后开始的页);本页图层到达才开始入页切换与动画计时,读取期间的单击与自动换片同此时基(worker 被占 400 毫秒的测试);切换结束即释放离开页的图层,只留当前与下一页;离开页的画法移入 `compose::left`,Metal 像素测试画出完成退出的对象在覆盖切换中不再出现(改回全部显示即失败);循环与失败由 `Player::tick` 判定(编辑区与放映共用),平台主线程测试覆盖真实播放器的循环边界与损坏文件。放映里异步失败交给提示这一步(`media_tick` 的错误分支)只能在主线程真实播放器上走到,应用单测在工作线程,未单独测到。R3 FAIL 两项已修,带回归与变异检验,未复审:人按的暂停与播到末尾分开记(`Player` 记手动暂停,循环只在真正播完后重播;定位改为精确,平台主线程测试在末尾 50 毫秒内暂停并持续 tick 仍停着);播放器之后报出的失败经 `Show::media_seen` 写出原因、停下并继续放映(应用测试,改为不提示即失败)。未验证:两块屏幕与演讲者视图(本机只有一块屏);真窗口里关闭后回到编辑窗口(修正延迟关窗后,本机当时有人在用,未复看);真窗口里的切换与动画放映、激光点、媒体播放;单屏开激光笔时系统光标仍显示(GPUI 无隐藏光标样式);Windows 与 Linux。
|
||||
- [~] C10.6 输出、演示准备检查(SHOW-13、14,QA-12)。已验证(macOS,带补丁 0003–0038 的 LOK):讲义 PDF 由内核按打印的讲义版式排出(每页 1、2、3、4、6、9 张,3 张带笔记线;只排范围内未隐藏的幻灯片;其他张数导出失败;内核测试按 pdftotext 页与文字核对,看过版面);导出(Cmd+E)PDF、图片、讲义,范围全部、当前、从–到;打印(Cmd+P)幻灯片或讲义后交系统打印面板;讲义与图片都在另存的副本上做,原文档的撤销步数不变(应用测试);图片每张未隐藏的幻灯片一个 PNG(宽 1920,浅色方案),任一目标已存在即不写(应用测试);PPTX/ODP 走保存与另存为(外壳已有);PPTX 存后重开,备注、切换、动画、媒体仍在并可改(内核测试)。演示检查(放映菜单“检查演示…”):`.uno:SlideCheck` 给每张幻灯片与母版的所用字体、链接文件与是否缺失、内核不能显示的对象,结合内核报的缺失字体与 `.uno:SlideShow` 的媒体,列出缺失字体、缺失媒体、外部链接、无法显示的对象及所在幻灯片或母版,点一行转到该页(内核与应用测试,共用夹具 `tests/fixtures/show/trouble.fodp`)。真窗口看过导出对话框。R1 FAIL 六项已修,带回归与变异检验:系统打印面板改在 GPUI 放开应用之后打开(`ely_office::print_later`,ElyDoc、ElyTable 同用;模态循环里点菜单原会因借用冲突崩溃);图片先以排他方式占下全部目标文件再画,任一已存在(含失效的符号链接)即不写并删去本次占下的,失败同样删去;输出的张数、隐藏与当前页取自标准幻灯片(`.uno:SlideShow`),母版视图下没有“当前”;读不出的图片按载入后的实际状态判定(链接先载入,解码失败的占位也算),OLE 链接的源文件缺失即标缺失(删去源文件的回归);检查与输出对话框的读取回来时,只有仍是最近一次请求且没有别的对话框打开才显示。打印面板的修复未在真窗口复验(本机当时有人在用)。R2 FAIL 三项已修,带回归与变异检验:PNG 编码后显式 flush 并报错(最后一次写入失败原被缓冲区析构吞掉,留下截断的文件);对话框或当前标签有任何变化,此前发起的检查与输出读取一律作废(另一个对话框打开后又取消,旧检查也不再弹出);图片的张数、隐藏与范围取自实际写出的副本(对话框打开后改了显示也一致)。审查者已在真窗口复验打印面板(幻灯片与讲义,面板里操作菜单、取消)不再崩溃。R3 FAIL 两项已修,未复审:范围的起止框键入即记下(原只在提交时记,失焦或直接按导出会丢掉键入的值;审查者以同一改法在真窗口验证,本项无自动测试,组件键入在应用单测里不可驱动);对话框打开后再显示隐藏页、导出图片得到三张可读 PNG 的回归(改回用对话框时的隐藏标志即失败)。未验证:真窗口里打印面板与讲义打印;Windows 与 Linux 的打印(`ely_platform::print` 只实现 macOS)。
|
||||
|
||||
## C11 ElyPDF(§11)
|
||||
|
||||
- [~] C11.1 PDF 工作进程与 MuPDF 绑定,C 垫片捕获异常;打开、渲染、阅读模式、搜索、书签(PDF-01)。已验证(macOS,MuPDF 1.28.5):worker 测试(渲染与深色、旋转、字符顺序与外框、搜索不分大小写、嵌套书签、坏文件报错后 worker 照常、关闭后文档不在、1000 页到第一块不到 3 秒);应用测试(打开带页数与书签、查找跨页逐个跳转与 Esc 关闭、跨页选择复制、切换单页仍在原页);布局、旋转换算、缩放档、选择与书签的纯函数测试;真窗口:打开 30 页文件、深色页面、放大、查找“2 of 30”并标出、双页。未验证:加密文件的密码流程(测试 PDF 待 C11.7 能写加密文件);200 MB 的真实大文件;Windows 与 Linux。 — R1 FAIL 十三项已修:测量层位置未绑视口(拖选无效);首次拖选丢起点;跳转后当前页回退;旧查找结果混入新查找;后台查找改动别的标签;画不出的块反复请求;单页命中超 512 被截;跨行短语算两处;第二个加密文件挤掉第一个;长页屏外块不丢;空书签仍占左栏;空白窗口即起 worker;截断的帧头当作流结束;另修垫片三处长注释并让注释检查覆盖项目 C 源码;各加回归,去掉修复时回归失败(左栏只测判定,渲染接线未单测) — R2 FAIL 四项已修:密码框里取消时下一个密码框建不出(窗口仍在事件中;现延后到事件结束,建不成即关闭该文档);双页时当前页被改回左页;横向屏外的块照请求照留;零页文件开出标签、单页视图越界崩溃(现报错并关闭,布局接受空页集);各加回归,去掉修复时回归失败 — R3 FAIL 五项已修,三轮用尽:提交密码时对话框的关闭回调放弃了文档(现关闭后对话框仍是这一份才放弃);连续密码框共用一个标识,第二个收不到 Esc(现按输入框实体区分);双页当前页不看横向(现只在两向可见的页里定);放大双页跳到右页不横移(现目标页左缘出视口即横移);视口右缘恰在图块边界时多取一列(现按半开区间);各加回归(真键入回车、两次 Esc、横滚、跳转、边界),去掉修复时回归失败;真窗口加密文件流程仍待 C11.7
|
||||
- [~] C11.2 页面组织(PDF-02)。已验证(macOS,MuPDF 1.28.5 加补丁 0001):外壳 `ely-office` 按引擎泛化(LOK 与 MuPDF),ElyDoc、ElyTable、ElyShow 全量照过(ElyDoc 加锁测试改为关外壳的密码框);worker 测试(删除后书签、链接、字段、页码标签,移动后书签跟随页,旋转、按所见边距裁剪与去裁剪,空白页与从文件插入,提取不改文档也不成重做,保存后撤销重做,坏编辑回滚);应用测试(删除后标签为未保存、撤销回到已保存,保存写入移动、保存后撤销为未保存、重做为已保存,编辑一次一条按序,编辑前的读取丢弃,关闭询问,提取与拆分写新文件、不覆盖打开的文件、目标已存在即一个不写,裁剪按毫米,点选、连选、增减);逐项变异验证;真窗口:缩略图栏、深色页面。组件库 2ce3368(`ContextMenu` 可定宿主大小)。未验证:拖动移动只经单测的同一入口(`move_to`),未在真窗口拖;修复后打开的文件不能保存(快照不支持,待 C11.8);200 MB 文件的保存内存峰值;Windows 与 Linux — R1 FAIL 八项已修:提取在原文档的撤销历史上做临时删除,冲掉重做(现在快照副本上做);结构编辑后区间点选的起点仍指旧页,插入空白页越界崩溃(现清掉起点);编辑失败不通知,等着的保存挂住(现失败也通知);窗口关闭在界面线程同步读历史、持锁关闭文档(现全在后台,关完再移除窗口;ElyDoc、ElyTable、ElyShow 同);从文件插入丢掉链接、批注与字段(现一并嫁接并改指新页,重名字段加序号);省略 /P 的字段控件不随页删除(现按被删页的 /Annots);裁剪不看视图旋转;裁剪不看 /UserUnit;各加回归,去掉修复时回归失败 — R2 FAIL 三项已修:重名字段的候选名用 256 字节定长缓冲,长名截断后永远同名,worker 卡死(现按需分配);插入的字段丢了源表单的默认外观与字体资源,合并后外观改变(现带过来,冲突的字体改名并改写引用;回归按红色像素比对渲染);一处注释超长;各加回归,去掉修复时回归失败 — R3 FAIL 两项已修,三轮用尽:改写字体引用不解 PDF 名称的 #xx 转义,转义写法的字段仍绑到目标的同名字体(现解码查表、转义写出);源表单的默认对齐 /Q 丢失(现与 /DA 一起给到字段树根);各加回归(保存后的字节),去掉修复时回归失败
|
||||
- [~] C11.3 文字对象编辑、图像与对象(PDF-03、04,§11.3)。已验证(macOS,MuPDF 1.28.5 加补丁 0001):worker 测试(块的文字、字号、可否编辑,表单里的字不可改;改字后旧字形不在、撤销复原;字体缺字时什么都不改、换中文字体写入;预览画出改动而文档不变;新文字落在点击处、颜色照设;图片与表单的移动、缩放、按比例替换、删除、添加与撤销;自带字体写入后再改;预览或撤销后再加同一字体仍正确);应用测试(点选块用自带字体改字不出预览;换字体先出预览、缺字提示;拖动图片与拉角缩放;新文字与删除对象;Esc 退出工具);去掉修复时回归失败(资源缓存);真窗口:编辑工具的轮廓、点选出面板、改字应用。用户定:字体用 MuPDF 内置,对象为图片与表单 XObject。未验证:页面内联图片(BI…EI)不可选;竖排与旋转的字不可改;Windows 与 Linux — R1 FAIL 七项已修:预览与缺字拒绝在开步后才查,冲掉重做(现检查都在开步前、预览在快照副本上);字号变大未出预览(现字号或范围变了即预览);同一基线的几段被当成两行,行距为零(现按不同基线);选文件期间换了标签,替换与添加图片落到别的文档(现绑定发起视图与版本);编辑落地时进行中的图层读取被丢弃,读取标记永远为真(现回来总清标记);文字框里按 Esc 不退出(上下文移到窗口根);注释超长,注释检查补上 .h 与多行 C 注释;各加回归,去掉修复时回归失败 — R2 FAIL 两项已修:预览确认不核对文档版本,删页后改写到别的页(现预览带版本,编辑落地即关,确认与执行前核对);选文件期间有排队的编辑,序号作废仍执行(现核对版本且须无排队编辑);各加回归(同文两页删首页后旧预览不写、排队时不执行),去掉修复时回归失败 — R3 FAIL 一项已修,三轮用尽:删除对象不核对版本与排队,连按两次删除删掉未选中的对象(现删除与拖动都须视图未变且无排队编辑);各加回归(连按两次删除只删一个、排队时拖动不入队),去掉修复时回归失败
|
||||
- [~] C11.4 批注、表单(PDF-05、06)。已验证(macOS,MuPDF 1.28.5 加补丁 0001):worker 测试(九种批注一步添加、读回种类颜色内容作者印章、高亮按颜色画出、撤销重做、存后另一 worker 读到;坏的批注连同好的都不加、重做不丢;按对象号改色改内容与删除、撤销回同一对象;新建文本、复选、两个同组单选、选择框,填写后保存重开值与选中保留、文本值画出;重名、点号、非组的单选名、空选项拒绝,改名撞名拒绝,组里最后一个按钮删去即删组;选择框换选项保留或清空原值;填写在撤销重做之间照样画出);应用测试(拖选高亮一步、作者为账户名、撤销;矩形与手绘按拖动落位、单击不画椭圆;便签与印章先问后加、单击选中改色删除;拖出复选与选择框后单击勾选与取消、右栏填选择框与原有文本字段;第二个单选默认并入本页组、组内只一个选中;排队编辑时单击复选框不入队;左栏批注读完全部页、点行转页;表单工具改名改选项、无改动不成一步);变异验证(排队守卫、开步前检查、删组);真窗口:高亮画出、左栏批注列表、右栏颜色与作者、表单菜单画复选框、阅读时单击勾选。组件库 3d08744(`DescriptionList` 的值占满所在列)。未验证:已有文件里的签名字段与按钮只读不改;多选列表框只当单选填;Windows 与 Linux — R1 FAIL 三项已修:子字段改名按父路径下的全名查重(原从根查,与兄弟字段同名),右栏只改名字末段;文字未读到时拖出的高亮等文字到后再加(原直接丢弃),换工具、新的按下或编辑即作废,读取失败即报错;新字段默认名按全文已有字段取(原只看本页),右栏等全部页读完再给;另修:右栏在拖动或绘制期间不打开(原按下即开,视图变窄,页面在手下移动,C11.3 的拖图测试约三成失败);单击按窗口像素判定(原按页面点,手下页面平移时误判为拖选);各加回归,去掉修复时回归失败 — R2 FAIL 两项已修:默认名也避开字段的父名(原只比完整名,有 `Text1.Child` 时仍给 `Text1`);已开的右栏在手势中保持开着(原手势中一律收起,页面在手下移动);另修根因:手势开始时冻结该页在窗口中的位置与比例,拖动与绘制都按它换算(右栏动画中开始的拖动也不再错位);各加回归,去掉修复时回归失败 — R3 FAIL 一项已修,三轮用尽:没有页面控件的字段(如只在字段树里的父字段)仍占名,默认名照样撞上(现 worker 读整棵 `/AcroForm/Fields` 的全部名称,右栏等它读完再给);加回归(worker 列出无控件的父与子、应用默认名避开它并能添加),去掉修复时回归失败
|
||||
- [x] C11.5 文档结构、OCR(PDF-07、08)。已验证(macOS,MuPDF 1.28.5 加补丁 0001、0002,tessdata_fast):worker 测试(扫描页按行识别出英文两行、位置为页面点;写入隐藏层后可查、可搜、画面逐像素不变,一步撤销重做,存后保留;重新识别替换旧层、不替换则并存;BMP 外字符、空行、越界页拒绝且不改;简体与繁体中文识别;坏语言报 Tesseract 错误后 worker 照常;书签加在末尾与子项之后、改名改目标、按标题核对、删除连子项、撤销、存后保留;链接列出内部目标、加网址、改到页、地址已变即拒、javascript 与 file 拒绝、删除;属性读写、生成程序不可改、存后保留;附件按名列出与读出字节、重名拒绝、移除、存后保留);应用测试(单击链接跳页;链接工具拖出后坏目标不写、网址写入、单击已有链接改到页与删除;书签以页名为默认标题添加、选中后改名与删除;属性只写改过的,无改动不成一步;附件栏列出、另存为写出原字节、移除;OCR 一页读完后一步写入可查,再识别时跳过已有文字的页;取消即不写);变异验证(OCR 跳过策略、属性只写改过的、补丁 0002 两处);真窗口:识别文字对话框、扫描页识别后查找命中。未验证:XMP 元数据不随属性改;Windows 与 Linux 上的 OCR — R1 FAIL 六项已修:OCR 层的 3 Tr 漏到之后写的字,新字不显示(现层内 q … Q);范围字段只在提交时更新,键入后直接点识别仍用旧值(现随键入更新,真窗口键入 2 后只读第 2 页);取消的识别回来的页会进入新一次识别(现每次识别带编号);多语言里有一种载入失败仍报成功(补丁 0003,载入数少于请求即报错);批注里的附件不在附件栏(现一并列出、读取、移除,以键区分);缺 /Size 的附件显示 0 B(现按实际字节);各加回归,去掉修复时回归失败 — R2 FAIL 一项已修:读附件时取走了文档自己持有的流缓冲区,再读为空、保存后附件内容丢失(现复制字节);加回归(连读两次、读取不改历史、存后重开字节完整),去掉修复时回归失败 — R3 PASS
|
||||
- [~] C11.6 内容删除与文档清理(PDF-09、10,§11.4,QA-14)。已验证(macOS,MuPDF 1.28.5 加补丁 0001 至 0003):worker 测试(删去“Hello”与其上的隐藏文字后,输出文本不含二者、其余文字保留、文件字节里无该词、只有一个 startxref,打开着的文档历史不变;图片被区域盖住的一半全黑、另一半保留颜色;区域内的便签删去、远处的保留;空区域与越界页不写文件;清理前计数,全选清理后各类为零、属性无标题、无附件、无隐藏文字、可见文字保留、原文档计数不变;只清批注时属性保留;未勾选报错);应用测试(区域标记与单击取消;文字标记写出新文件后无该词、原文档无编辑;编辑后标记清空、无标记时不开对话框;清理先计数再写出、标题已去);变异验证(删去区域内批注、编辑后清空标记);核对本身抓出过两处:判字相交须与删除同一缩框规则(原把邻字算作残留),并证实隐藏文字随删除一并去掉;真窗口:区域标记的深色框与删除对话框。未验证:Windows 与 Linux — R1 FAIL 八项已修:部分盖住的路径留在文件里(现碰到即整条删,核对加设备重放,区域内除黑框外有画即失败;图片单独画出只许白或黑);字段删控件后空父字段带值留下(现一并去掉);关着的图层与零透明度文字未清(现过滤器设 View 用途,删图层内一切与看不见的字);页面上的 XMP 未清(现清全部对象的 /Metadata 与 /PieceInfo);无 /Names 时清理报错(现判空);隐藏批注两头不计(现计入隐藏内容);扫描回来不核对标签与版本(现用 ask_in 并核对版本,对话框写文件名);路径选择期间改了标记仍写(现确认时绑定标记与版本,变了不写);各加回归,去掉修复时回归失败 — R2 FAIL 六项已修:删字后标记内容的 /ActualText 留下(现有区域的页去掉标记内容与结构元素的替代文字);重叠区域后者删掉前者的 Redact 批注(现先删后建);/AF 关联文件、嵌套直接字典里的元数据、链接与父字段上的脚本、/Next 链里的脚本漏清(现一遍遍历全部对象与嵌套值,脚本从链中摘除、后续接回);各加回归,逐一变异验证失败 — R3 FAIL(三轮用完)四项已修:子项 MCR 指定页的结构元素替代文字留下(现按子项 Pg 判定并连同上级);页面 /Thumb 引用原图(现删去,核对查缩略图);隐藏文字的标记内容 /ActualText 留下(现有隐藏文字被删的页去掉替代文字);隐藏链接与隐藏字段漏清(现按 /Annots 删链接,控件连同字段值删);各加回归,逐一变异验证失败;未经第四轮审查
|
||||
- [~] C11.7 密码、签名(PDF-11、12,QA-15)。已验证(macOS,MuPDF 1.28.5 加补丁 0001 至 0004,OpenSSL 3.6 静态链接):worker 测试(AES-256 保护副本要密码、只许所给权限,越权请求以权限名拒绝;错的所有者密码不改访问,AES-128 样本上错一次后文字照读;对的解除限制;只凭用户密码不能改保护,所有者可去掉保护;所有者密码必填且不同于打开密码;无打开密码的文件照守限制;新签名摘要未改、签名者与原因地点读回、证书即所给证书;证书密码错不写文件;改一字节即为已改;二次签名后首个仍未改且标为其后有更新;空签名字段原位签;不许改页面时不能加新签名字段;普通保存的已签文件不带签名、字段留空;键入签名以嵌入的 Great Vibes 写入、缺字报出该字);系统信任测试(自签名证书不受信任、以其为锚即受信任);应用测试(限制在所有者密码前拦住旋转、错密码再问、对后可旋转;禁止复制时剪贴板不变;保护对话框要求不同的所有者密码并写出加锁副本;已签文件为签名风险、签名对话框读出未改与不受信任并画出;签名写出副本、原文档无编辑;拖框后有编辑即不签;手写、键入、图片三种可视签名各落为墨迹、文字、图片且按比例;对话框把手写交给放置工具);逐项变异验证(所有者错后重认证、权限核对、复制拦截、整份写出去签名、签名前核对版本);真窗口:已签文件的提示条与状态、签名对话框(曾抓出列表行含换行即崩溃,改为对话框分行)、可视签名对话框(组件库 a6b1bb0 让签名板文案进中英目录)。未验证:Windows 与 Linux 的证书信任;时间戳与吊销;在真窗口拖框签名与放置可视签名(同入口经单测) — R1 FAIL 五项已修:只按页面控件列签名,漏了不在页面上的签名字段(现按表单字段树列,页可无;无 /P 时从页面 /Annots 找);另存为已签文件后打开的文档仍带签名(现保存时同一步去掉内存里的签名值,不算编辑,列表重读);签名完成清掉当前标签的位置(现只清发起视图的同一位置);所有者密码回复关掉后开的对话框(现只关同一输入框的对话框);补丁注释超长且检查漏看补丁里的块注释(现缩短,检查覆盖);各加回归,逐项变异验证失败 — R2 FAIL 两项已修,根因同一:上轮在保存时去掉打开文档的签名,写入失败即坏了原签名、撤销重做又带回签名;现签名一律从文件读(`SIGNATURES` 带路径,另开文件核对),列表按标签当前路径读,内存去签名删去;加回归:另存失败后仍为已签与原签名、另存后撤销重做列表仍无签名,变异(改回读编辑中的文档)即失败 — R3 FAIL(三轮用完)三项已修:另存为后签名仍用内存里带旧签名的文档(现已签文件另存成功即按新文件重开标签;去签名时清掉该字段自身的只读位,副本里的空字段可再签);签名列表按路径缓存,文件被外部改动后仍显示旧结果(现每次打开对话框重读);保存打不开输出文件后 MuPDF 的保存中标志留着、此后编辑不进撤销(补丁 0005);各加回归,逐项变异验证失败;未经第四轮审查
|
||||
- [~] C11.8 输出、文件恢复(PDF-13、14,QA-13)。已验证(macOS,MuPDF 1.28.5 加补丁 0001 至 0005):worker 测试(页面按 144 dpi 导出为两倍点数的 PNG、离谱分辨率报错;文字按页以换页符分开;优化副本小于原件十分之一、432 dpi 的图片降到 150 dpi(宽 205 至 212)、降到高于阈值报错;无打印许可不能出打印副本、无复制许可不能导出文字;损坏文件打开得修复报告、两页照读、编辑后能保存、原件字节不变,完好文件无报告;保存打不开输出后历史仍完整);PDFKit 读回改字、填写的字段与页序;应用测试(导出两页图片与文字;损坏文件为修复风险、有报告、报告对话框画出,另存到原件被拒且原件不变,另存新文件后按新文件重开、无风险);变异验证(不换修复副本、修复风险不报、另存后不重开);真窗口:损坏文件的提示条与状态、导出对话框。未验证:Windows 与 Linux 打印;真窗口实际打印;图片编辑经独立阅读器核对 — R1 FAIL 三项已修:另存后立即重开丢掉快照之后的编辑(现标签记下待重开,内存与文件一致时才重开,期间的编辑留着、下一次保存后重开);尾部完好而对象偏移坏时 MuPDF 读到才修复,漏报且保存失败(现打开与解锁后逐个读对象,修复都在打开时发生);降采样数值只在回车或失焦时生效,键入后直接导出用旧值(现随键入生效);前两项各加回归,变异验证失败;第三项真窗口复测:键入 72 后直接点导出,输出图片宽 100(即 72 dpi) — R2 FAIL 两项已修:结构完好而内容流解压失败时当作正常文件、导出空文字、保存覆盖原件(现打开时解码图片以外的数据流,有警告即为损坏、改开副本;导出遇警告报错);打印副本照原加密、打印面板打不开(现打印副本不加密,打印后删去);各加回归,逐项变异验证失败;真窗口:带打开密码的文件打印面板打开,取消后副本已删 — R3 FAIL(三轮用完)两项已修:扫描跳过图片,坏图片照样可原位覆盖(用户定:图片在画出时检查,没画过的在原位保存前检查;画出遇警告即转修复风险,保存前检查有问题即转另存为);导出图片在等编辑与选路径前取页码,删页后导出另一页(现绑定发起时的视图版本,变了即拒绝);各加回归(画出的坏图、没画过的坏图、排队删页),变异验证失败;未经第四轮审查
|
||||
|
||||
## C13 ElyTree(§13)
|
||||
|
||||
- [x] C13.1 Git 运行器(参数数组、porcelain v2)、仓库管理、工作区状态(TREE-01、02)
|
||||
- [~] C13.2 差异、暂存(TREE-03、04)。已验证(macOS,随附 Git 2.56.0):Git 自己的差异解析(无换行结尾、二进制、非 UTF-8、空白上下文行);按块与按行暂存和取消暂存,索引字节逐一核对(两块只暂存一块;只选一行加入或删去;末行无换行时的追加与恢复;用户 `apply.whitespace=fix` 不改所选字节);显示后文件变了即拒绝、索引不动;空旧文件不折叠;文件与链接互换两段都显示、整份暂存、行与行号逐一核对;悬空链接与指向非 UTF-8 文件的链接按链接路径读;图片与二进制按大小或图片显示;忽略空白时空白改动不显示;窗口内选行(Shift 取范围)、暂存后所选清空、换文件后旧读取迟到即弃;真窗口经 AX:选两行暂存,索引恰为所选,已暂存区显示实际索引内容,并排视图,各行、块按钮与分段在 AX 树中。未验证:Windows 与 Linux — R1 FAIL 八项已修:换文件后旧读取覆盖新文件(读取序号窗口内唯一);未选删去行留作上下文时照搬无换行标记,追加行被并入(按两侧实际行尾生成);`apply.whitespace` 改写所选字节(`--whitespace=nowarn`);差异外的非 UTF-8 字节被替换成 U+FFFD 仍进文本视图(两侧整份核对);`diff.suppressBlankEmpty` 使差异读不出(命令里覆盖);空旧文件被算成一行(零行);组件 viewer.rs 超 500 行(拆出 rows.rs);三条组件注释超十词。R2 FAIL 三项已修:取消暂存时所选删去行的无换行标记照搬,与后行相接(统一规则:无换行的行在哪一侧后面还有行就带上换行);文件变成符号链接时同一路径两段输出读不出(解析多段);rows.rs 一条注释超十词。R3 FAIL 两项已修(不再送审):类型变化两段共用折叠游标,已删除内容又显示为未改动(多段时不折叠);工作树侧跟随符号链接读目标(按 Git 读链接自身的路径字节)。
|
||||
- [~] C13.3 提交、历史、Blame(TREE-05、06)。已验证(macOS,随附 Git 2.56.0):历史读分支、标签、合并,分页,按信息、作者、提交号、改动文字搜索;文件历史跨重命名;提交详情(根提交、重命名、新增)与其文件差异;Blame 含未提交行;提交与修改最近提交,`#` 行在用户 `commit.cleanup=strip` 下照留,钩子失败报出原话;身份与签名取自 Git 设置;窗口:提交框提交后历史自动重读、选提交与文件,提交框按仓库各存一份,连按两次修改最近提交读回作废,提交期间键入的保留,Blame 只取最新一次读取,历史读取期间的外部提交读完后补上,引用读不出时报错、修好后恢复,外部改身份后重读、身份对话框字段有无障碍名;真窗口经 AX:历史页、提交详情各字段、差异行与 Blame 行的文字、文件历史。未验证:Windows 与 Linux;实际 GPG/SSH 签名 — R1 FAIL 八项已修:提交框与修改最近提交各仓库共用(按仓库存);修改最近提交的读取迟到又打开(作废序号);提交完成无条件清空(只清仍是所提交内容的);文件历史与 Blame 回调只比路径(每次打开的序号);`commit.cleanup=strip` 删 `#` 行(`--cleanup=whitespace`);历史里的文件无文件历史与 Blame 入口(文件标题栏);提交详情不进 AX(`DescriptionList::text` 等);组件注释超十词。R2 FAIL 三项已修:勾选在读回前不显示,第二次点击仍是勾选(立即勾选、读回前不能提交);只读差异行不进 AX;Blame 行不进 AX。R3 FAIL 五项已修(不再送审):历史读取期间的外部更新漏掉(记下、读完再比);引用读不出只写日志(显示原话,读通即清);外部改身份与签名不重读(状态重读与窗口回前台时重读,取最新);身份对话框输入框无无障碍名(以标签命名);组件注释超十词。
|
||||
- [~] C13.4 分支、标签、合并、变基、冲突(TREE-07、08、09)。已验证(macOS,随附 Git 2.56.0):分支与标签的建、改名、删(每次先问)、切换(远程分支按完整引用跟踪,不被同名标签劫走);合并、拣选冲突停下不弹失败框,提示条写明两侧;按块取一侧、保存、标为已解决、继续得到合并提交;标记宽度照 `conflict-marker-size`(5、10,以及 0、-1、abc、10abc 按 Git 当作 7),正文里形似分隔的行照留,只按 LF 断行;diff3 空基底不被补填;含 NUL 的冲突整份解决;钩子拒绝(继续时的 pre-commit、取一侧时的 post-checkout,输出含 CONFLICT 字样也算)显示 Git 原话;真窗口:冲突提示条、三侧、按块取另一侧。未验证:Windows 与 Linux — R1 FAIL 七项已修:符号链接与二进制冲突按文本处理(整份解决);保存不经写队列、顺着符号链接写(入队、拒绝链接);钩子失败被当成冲突停下(按未合并条目判定);冲突分块另算一套合并(照 Git 的标记);同名分支与标签混淆(完整引用名);行内动作未接;窄时删除按钮被挤出。R2 FAIL 六项已修:标记宽度写死 7、`=======` 前缀行当分隔(按属性宽度、整行判定);仍有未合并条目时钩子失败被当成停下(按命令与 Git 的 CONFLICT 行判定);NUL 的 UTF-8 文件当文本(按 Git 视为二进制);zdiff3 基底覆盖 diff3 空基底(只填缺的);远程分支切换被同名标签劫走(`refs/remotes/`);组件注释超十词。R3 FAIL 三项已修(不再送审):`conflict-marker-size` 为 0、负数或非数时读错(照 Git 的 `strtol_i`,不合即 7);正文里的独立 CR 被当作行界(组件 a284c71 按 LF 断行);任意含 CONFLICT 子串的钩子输出被当成停下(须是合并类命令、输出有以 `CONFLICT (` 起头的行、且索引仍有未合并条目;Git 的报错带上 stdout 与 stderr 两者,拣选的 CONFLICT 行在 stdout)。
|
||||
- [x] C13.5 Stash、远程、worktree、submodule、LFS(TREE-10、11、12)。已验证(macOS,随附 Git 2.56.0 与 git-lfs 3.8.0):储藏的列表、说明与分支、未跟踪文件分开显示与其差异;弹出遇冲突保留储藏;储藏列表变了即拒绝;远程的地址与推送地址;强制推送列出将覆盖的提交,远端在计划之后变了即被租约拒绝,取回后再推成功;没有上游或游离不给强制推送;工作树的分支与失效、清理;子模块未检出与在记录的提交上;LFS 对象在本机与只有指针;窗口:储藏对话框、储藏详情、删除先问、弹出;取回带进度落地;停止取回不报失败;无上游推送先问远程并设上游;强制推送对话框;设置与取消上游;真窗口:仓库页各列表、储藏与工作树详情。未验证:Windows 与 Linux;真实网络凭据;LFS 对象下载 — R1 FAIL 六项已修:强制推送确认后本地分支再动会推出未预览的内容(推送所示的源提交);对话框提交时写进当前标签而非打开时的仓库(各表单带仓库号,含 C13.4 的分支、标签、改名与身份对话框);按冒号前缀合并输出删去钩子的报错行(只合并回车改写的行);推送的远程名前无 `--`(加上);空说明的储藏使整个列表读失败(按三字段解析);行的焦点框不画、行内动作只在悬停时显示(组件 bc6f1c2 与 3741d5d:透明边框、动作一律显示)
|
||||
- [~] C13.6 恢复与清理、外部工具、操作规则、外部 Git 响应(TREE-13、14,§13.3,QA-19、20)。已验证(macOS,随附 Git 2.56.0):引用日志的位置、原因与先后;把分支移回保留工作区改动,HEAD 在预览后动过即拒绝;清理预览含制表符、引号、中文名与文件夹,只删所列,预览后新建的与被忽略的留下;锁文件在时对话框重试、锁文件不被删除、去掉后重试成功;凭据被拒显示 Git 原话;变基中断、中止后回到原处;窗口:清理对话框、引用日志选中与移回;编辑器设置的读写与损坏不覆盖;真窗口:工具行、清理预览、文件标题栏按钮与启动 ElyMark。未验证:Windows 与 Linux 的终端;ElyMark 打开文件的内容(C14) — R1 FAIL 八项已修:清理把预览的文件夹整个交给 Git,预览后其中新建的文件也被删(展开为文件,只删所列,空了的文件夹才删);`reset --keep` 丢掉独立暂存的内容(有暂存即拒绝,执行前再查);移回只核对提交,切到同一提交的另一分支也会被移(另核对完整分支引用与游离);引用日志时间取了提交时间(取该次移动的时间);同一提交的多条日志一起选中、详情取错条(按条目选);编辑器设置字段类型不对被当成未选并覆盖(报错不覆盖);`open -a` 的失败被丢掉(等它结束并显示);配置文件锁不认作锁(加上其原话)。R2 FAIL 两项已修:所列文件在确认前被换成同名文件夹时 `git clean` 递归删去其中新文件(先查类型、变了即整体拒绝,由 Ely 逐个删文件);刷新后按旧位置取引用日志条目,选中与详情漂到别的事件(按事件本身认定)。R3 FAIL 三项已修(不再送审):直接删文件时不管确认前已被跟踪的文件(改回 `git clean -f -- 所列文件`,Git 留下被跟踪的);父文件夹被换成指向仓库外的链接时删到外面(逐段核对无链接、类型不变,变了即拒绝);同一秒同原因的两条引用日志无从区分(加自最早起的序号)
|
||||
|
||||
## C14 ElyMark(§14)
|
||||
|
||||
- [~] C14.0 本地 Web 内容宿主 `ely-web-content`:CEF 离屏接入 GPUI,输入、焦点、缩放、主题 token(§17.3;ElyMark 先用,ElyMail 后用)。已验证(macOS arm64,CEF 154.0.32):开发构建与 `.app` 包(五个辅助应用)里页面画出、随应用深色;点选、中英文键入、回车、退格、方向、拷贝粘贴;输入法组字与提交、焦点、主题变量、缩放 1.25、`elyQuery` 往返、页面跳不出所在文件夹(`tests/scripts/webview.sh`);连续五次启动都画出。未验证:Windows 与 Linux;辅助技术读页面文字(缺口) — R1 FAIL 七项已修:资源请求不过门(每个请求都判定,外面的脚本与 HTTP 被取消);符号链接可出门(按真实路径);浏览器与表面互相持有,关闭后不释放(关闭时断开,20 个都释放);载入失败当作载入完成且显示 Chromium 的错误页(报失败,显示组件错误视图与重新载入);文件名里的 `#` 当作 URL 语法(逐字节转义);渲染进程退出与 CEF 起不来只写日志(视图内显示故障并可重新载入);测试不量像素(截窗口量标记色,去掉绘制即失败)。另修:在 GPUI 更新里同步调 CEF 偶发中止(全部经主线程队列在更新之外执行)。R2 FAIL 两项已修:WebSocket 不经资源回调可达本机(每个 CEF 进程把一切网络送往不通的代理,含回环);崩溃测试不看故障视图(截窗口须不见旧画面,经辅助功能按下 Reload 才恢复)。R3 FAIL 两项已修(不再送审):WebRTC 经 UDP 绕过代理(只许经代理,且页面里删去 WebRTC 构造;本机 UDP 收不到 STUN,去掉时测试失败);恢复只看载入事件(重新载入后截窗口须见页面标记,保留故障时测试失败)
|
||||
- [~] C14.1 内容包:Milkdown、CodeMirror 6、Mermaid、数学公式,本地构建、随包、无 CDN。已验证(macOS):`tests/scripts/mark-content.sh` 构建后在真窗口里三种模式渲染标题、任务、表格、脚注、公式、代码块、Mermaid 与两种错误(带原文与位置),每个代码块滚入视口后读出、要求可见(不透明、未被遮挡),图不小于 100×80,预览只读,切换深浅色后图的颜色随之变(含首次绘制途中切换,且首次落下的就是新颜色),绘制途中把代码块改成 Rust 后旧图不落入,连续两次显示只留后一次且只报后一次,截窗口时背景色占比须低于 0.97(隐藏正文时为 0.999),KaTeX 字体载入,无越界请求;`.app` 包内页面显示。未验证:Windows 与 Linux;树莓派载入时间 — R1 FAIL 四项已修:按钮图标的尺寸规则也压扁了 Mermaid 图(限于工具栏与语言列表);主题变化后已显示的图不变色(图带编号,令牌一变即经代码块的回调重画,只取每块最新的一次);连续两次显示会留下两个编辑器(每次显示一个容器与序号,过期的创建即销毁、不报告);测试不看是否真的可见(逐块可见性、图的尺寸、换主题、竞争与截窗口,各以变异核对会失败) R2 FAIL 五项已修:Milkdown 每次预览给的回调都是新的函数,按回调认定的记录对不上,改语言后旧图仍落入(预览先返回带编号的占位,结果只在占位仍在页面上时落下;改语言、改文字、卸载都会换掉占位);首次绘制途中换主题时记录被当作已离开而删去(占位在页上即保留,换主题时连同待画的一并重画);记录由全局持有、编辑器销毁不释放(每个编辑器拥有自己的记录,销毁即释放;占位离开页面的记录在下一次绘制或换主题时清掉;实测 20 次显示与切回源码后为 0);测试的退出钩子在探针已回收后返回 1(只停仍在运行的探针,清理总执行);块公式只查节点存在(滚入视口查可见,隐藏公式的变异会失败)。重载页面使两项竞争发生在 Mermaid 包载入与绘制途中,去掉各项修复的变异都会失败 R3 FAIL 两项已修(不再送审):主题重画复用同一记录,旧主题的首次结果仍会先落下约一秒(每次绘制带代次,重画即作废此前的结果);编辑器销毁后队列里排着的绘制仍逐个执行(每个绘制在调用 Mermaid 前与落下前都核对属主、占位与代次,失效即跳过;测试在三张约 3 秒的图排队时切回源码,之后的主线程长停顿须不超过一次,去掉核对时为两到三次)
|
||||
- [x] C14.2 文件会话与原文保留:BOM、换行、front matter、未知语法(§14.3,QA-21)。已验证(macOS):`tests/scripts/mark-session.sh` 在真窗口打开 BOM、CRLF、front matter、Setext 标题、`1)` 列表、HTML 块、`~`删除线、硬换行、链接定义与 `[[...]]` 的文件,不改保存逐字节相同,首段键入一字保存只改该段(整篇序列化与去掉 BOM 的变异都失败),不能对应的文档以源码模式打开并可保存;`text.rs` 单测(BOM、换行、混用、非 UTF-8)。未验证:Windows 与 Linux;混用换行的文件编辑后统一为多数的换行(已知限制) — R1 FAIL 六项已修:改动段落时其中的 `[[...]]` 被转义、引用与列表内的局部改动重写整个容器(Milkdown 不能逐字写回的顶层块一律以原文块显示与编辑,序列化照文档自己的写法);块间隐藏的链接定义被移动与修剪(块间原文也成原文块,无隐藏原文);页面重载后新编辑被当作旧版本丢弃(每次载入一代,回报带代数,计数归零);无读权限的文件被当作加密文件问密码(权限错误照原话报出);注释超过十词 — R2 FAIL 两项已修:保存写出的文字与随后读的修订标记取自两次快照,导出与读标记之间的编辑被记为已保存(写出时记下所写文字的标记,读修订时先交它;单测在两者之间改文字);链接定义的测试用多行 grep 恒真(改为逐字节比较)
|
||||
- [~] C14.3 编辑模式、Markdown 与扩展语法(MARK-01、02、03、04)。已验证(macOS):`tests/scripts/mark-modes.sh` 真窗口里文档旁图片画出,可视、源码、预览往返不改文件,预览不收键入,经预览往返后撤销仍有效,源码改动回到可视并保存,重命名拒绝已有名称、改到新名后继续保存,新建为空可视文档;`mark-content.sh`、`mark-session.sh` 照过;`rename_new` 单测。未验证:Windows 与 Linux(改名的平台实现未构建);“直接打开文件夹”随文件树在 C14.4 — R1 FAIL 七项已修:另一模式改过文字后回来,原编辑器连同撤销历史被销毁(旧编辑器留在新编辑器之后,撤销用尽即回到它,重做用尽回到较新的);改名完成时标签已关仍去登记(须仍开着且仍是原路径);Windows 路径的 file URL 无效(驱动器与 UNC 分别处理,围栏反向转换,单测);构建中再要的模式被丢(记下,构建完显示最后一个);新建的测试不看内容(读外壳写下的文件须为空);`handlers.rs` 超过 500 行(请求处理移入 `fence.rs`);`Renaming` 只有一个使用者(移回 ElyMark) — R2 FAIL 四项已修:跨模式撤销被当作新编辑、丢了重做(跳转只报新文字,不截时间线);按模式分开的历史使撤销次序不对(改为本文件一条时间线,以当前模式重建);改名间隙里打开新名字得到第二个可写标签(登记记下文件身份,原路径不在而身份相同即同一文件,改名后迁移登记;单测);构建完成的回报回来后旧请求覆盖新的(请求按序号,只认最新) R3 FAIL 三项已修(不再送审):编辑器内撤销后切换模式,重做丢失(离开时把全部重做后的文字记在时间线前方);原子保存换了文件身份,保存后改名的间隙仍得到第二个会话(外壳每次保存成功后以 `Sessions::track` 更新登记的路径与身份;单测在原子替换后改名);旧回报清掉了较新构建排队的模式(代次不符即退出,不碰排队请求;探针按住两个回报复现)
|
||||
- [~] C14.4 导航与搜索:文件树、大纲、多标签、最近文件、文件夹搜索(MARK-05、10)。已验证(macOS):`tests/scripts/mark-places.sh` 真窗口打开文件夹与文件,树只列 Markdown、回车打开,大纲跳转后键入落在标题,文件夹搜索找到两文件的行、不搜 .txt、结果打开到该行,最近列表重启后保留并可打开文件夹;`folder.rs` 与 `ely_files::recent` 单测;其他三个 ElyMark 门照过。未验证:Windows 与 Linux;大纲项的无障碍(组件库待办) — R1 FAIL 六项已修:搜索定位只到块首(带上查询,落在块内该行那一处);可视与预览的行号未计 front matter(按 front matter 行数换算,落在其中即滚到它);以原文块显示的标题不进大纲(原文块记下标题级别,大纲取其首行文字,跳转落在标题文字上);本文件的搜索结果不随标签切换(切换即重搜,迟到的结果按次序作废);读不出的文件只记日志(以“某文件未能搜索”的行列在结果里,带原因);改名后最近列表仍是旧名(最近列表按真实路径,改名即迁移,旧路径不在即移出)。各项去掉时 `mark-places.sh` 或 `mark-modes.sh` 失败 R2 FAIL 三项已修:块内命中按源码计数,链接地址里的词打乱次序(此前行先经 Milkdown 解析,按编辑器所见的文字计数;原文块照源码);引用、列表里的标题成了原文块后不进大纲(原文块的源码以 remark 读出其中全部标题与位置,不再只记顶层标题的级别);改名后文件树与文件夹搜索仍是旧名(改名即重列文件夹,列表变了即重搜文件夹)。各项去掉时 `mark-places.sh` 失败 R3 FAIL 两项已修(不再送审):此前命中的计数与块内查找的重叠规则不一(都按不重叠,`ana` 在 `banana` 里只算一次);命中只在链接地址里时落到下一处可见的命中(该行所见文字没有查询词即落在该行开头)。另修:已打开的文件因路径写法不同(`/var` 与 `/private/var`)不被认出,跳转落空(文件夹以真实路径列出,标签按真实路径认);块内位置把软换行算进与不算进两边不一(编辑器一侧也算入换行)。去掉各项时 `mark-places.sh` 失败
|
||||
- [~] C14.5 编辑工具与附件(MARK-06、07)。已验证(macOS):`tests/scripts/mark-tools.sh` 真窗口可视全部替换、源码正则替换、Tab 与 Shift-Tab、列表续写、Cmd+Down 后插入表格与行列操作、拖放图片落到 assets,`ELY_CLIPBOARD_TEST=1` 时粘贴图片;`edit.rs`、`attach.rs`、`import_attachment` 单测;其他 ElyMark 门与 `webview.sh` 照过。未验证:Windows 与 Linux — R1 FAIL 五项已修:查找框整窗一个,替换用了该标签页面里旧的查询(下一处与替换带上框里的查询,换标签即重查);键入替换文字使命中后移(查询变化从命中起点找);源码里替代文字的方括号未转义(加转义);复制期间另存到别处,链接指向新文件夹里不存在的图片(完成时核对文件夹,变了即不写链接并说明);四处注释超过十词(缩短,`comments.py` 另查 Swift 与页面 JS)。去掉各项时 `mark-tools.sh` 失败 R2 FAIL 三项已修:空查询仍调 CodeMirror 的查找命令,打开它自带的面板(各命令先核对查询有效);正则校验未带编辑器所用的 `u` 标志,`a{`、`\a` 报成无结果(按 `gmu` 校验);`comments.py` 漏过 JS 的跨行块注释(一并检查,`comments-rules.sh` 核对)。`mark-content.sh` 的 find 步核对两种模式都无 CodeMirror 面板、四个错误;去掉守卫或 `u` 时失败 R3 FAIL 两项已修(不再送审):可视模式零宽正则在表情字符处只进一个 UTF-16 单元,落在代理对中间时同一命中反复收集,页面进程耗尽内存崩溃(零宽命中按码点前进,命中不再往前即停);`comments.py` 漏过代码之后开始的跨行块注释(任意位置未闭合的 `/*` 都算)。`mark-content.sh` 在两种模式下对 `😀 alpha` 找 `(?=.)` 得 7 处(只进一个单元时页面崩溃、测试失败),`comments-rules.sh` 核对行内起头的跨行注释
|
||||
- [~] C14.6 外部变更与会话恢复(MARK-08、12,§14.4,QA-22)。已验证(macOS):`tests/scripts/mark-outside.sh` 真窗口原子替换后重载并保持位置、键入后 1 秒自动保存;未保存时外部写入不被覆盖,三方比较、保留你的、合并;外部改名跟随、删除后不自动保存不重建、读不出时提示并可再查;退出后恢复标签、模式与行;崩溃后恢复未命名文档的未保存内容并标为未保存,显式不保存后不回来;`Sessions::renamed_in`、`alive` 单测;其他 ElyMark 门与 `webview.sh` 照过。未验证:Windows 与 Linux;网络目录;导出(C14.7) — R1 FAIL 六项已修:比较期间磁盘再改,合并与保留按旧的磁盘指纹写入、覆盖新改动(比较记下所读内容的指纹,选定前核对,不符即重读重比;重载与比较都以所读字节的指纹为准,`Session::reload_at`);有路径文件的崩溃恢复内容直接进了原文件、下一次编辑自动保存覆盖原件(恢复内容一律开成未命名副本,原件照磁盘打开);原件已不在时恢复内容随标签被丢(照样开成副本,并提示哪个文件没能打开);取消退出后会话清单一直不更新(外壳的“未保存”询问取消时调 `Host::close_cancelled`,清单恢复跟随;已关闭或放弃的标签的恢复点随即删去);只恢复行、不恢复行内位置(记下并恢复列);“保留你的”之后自动保存一直停着(选定后重新开始)。另:启动参数给了文件时只开它(及崩溃恢复的副本),不再连带上次的标签(否则加载先后决定哪个标签在前,键入落到别的文件)。去掉各项时 `mark-outside.sh` 失败 R2 FAIL 五项已修:“保留你的”仍走无条件覆盖(改为以所比较的指纹为基线 `Session::rebase` 后走普通保存,之后的外部改动照样冲突);一个标签连续消费多份恢复文本(每个标签只在首次显示时恢复一次);询问里点保存、随后取消保存面板(或保存失败)时清单仍冻结(外壳在这些出口也调 `close_cancelled`,冲突询问取消同);放弃的标签要等下一次定时才删恢复点与清单(外壳关闭标签时调 `Host::tab_closed`,即刻删去其恢复点、在途的恢复点写完即删,并更新清单);旧会话文件缺列即启动失败(缺列读作 0)。另:自动保存撞上外部改动时不弹外壳的冲突框,交给比较(`Host::conflicted`);组件库 f756043:脏标签与非当前标签的关闭按钮原为隐藏(`invisible`),辅助技术按不动、无法关闭,改为透明仍可按;`ax-press` 另找应用的各窗口并可限定角色。去掉各项时 `mark-outside.sh` 或单测失败 R3 FAIL 三项已修(不再送审):外壳冲突询问的“保留我的”仍以 `save_overwrite` 无条件覆盖,导出期间的新外部写入被盖掉(改以此刻磁盘的指纹为基线 `rebase` 后走普通保存,之后的变化照样冲突;文件已不在时才直接写;ely-files 单测核对其后的写入不被覆盖,ElyDoc 的保留我的测试照过);退出时冲突询问的“从磁盘重新载入”丢了挂起的关闭、清单冻结(先调 `close_cancelled` 再重载);退出途中首次保存的未命名标签在清单里仍无路径,重启不恢复(关闭钩子按恢复槽把新路径与名称写回冻结的清单)。`mark-outside.sh` 覆盖退出时冲突→重新载入→新文档进清单、退出时首次保存→重启恢复该文件;去掉各项时失败
|
||||
- [~] C14.7 书写视图、输出、Git 配合、大文件(MARK-09、11、13、14)。已验证(macOS):`tests/scripts/mark-output.sh` 真窗口专注模式与外观持久、HTML/资源文件夹/PDF/纯文本导出全文、打印出 PDF、Git 状态随保存变化并打开 ElyTree、3.3 MB 文件只用源码且全文保存与导出、富文本复制带 HTML;`mark-content.sh` 的 look 步;`paper`、`look` 单测;PERF-07 release 1 MB 1.4 秒(冷启动 2.0 秒);其他 ElyMark 门与 `webview.sh` 照过。未验证:Windows 与 Linux R3 FAIL 两项已修(不再送审):导出只收 `img[src]`,`srcset` 与 `picture > source` 的资源留着相对路径(导出时每张图定为一个 `src`:去掉 `srcset`、`sizes` 与 `picture` 的 `source`,无 `src` 时取 `srcset` 的首个地址,随后照常内嵌或拷入资源文件夹);KaTeX 的语法错误被吞,导出照报完整(保留渲染的 VFile,公式错误带行号与 KaTeX 的说明进提示,日志只记行号)。另:组件库 28a6bba 的 `Toaster` 原把每条提示的标题写进日志(提示可引用正文与图片名),改为只记编号与级别;外壳加 `toast_unlogged`,ElyMark 的缺失提示用它。`mark-output.sh` 加响应式图片三种、错误公式、图片名与公式文字不进日志;去掉各项时失败
|
||||
|
||||
## C15 ElyAgent(§15)
|
||||
|
||||
第二批发布(用户定,2026-10-08):首版不发布 ElyAgent,首版的完整测试与打包不等它。
|
||||
|
||||
- [ ] C15.1 嵌入 Kigi CLI 的 Rust 代码作后端,沿用其提供商登录与模型配置,不用 ACP(§15.2)
|
||||
- [ ] C15.2 工具管理、配置管理、配置保护(AGENT-01、02、03)
|
||||
- [ ] C15.3 会话、输入输出、并行运行(AGENT-04、05、06)
|
||||
- [ ] C15.4 工具与授权、文件与产物(AGENT-07、08)
|
||||
- [ ] C15.5 会话恢复、执行控制、历史、用量(AGENT-09、10、11、12)
|
||||
- [ ] C15.6 本地模型、托盘、生命周期(AGENT-13、14,§15.4,QA-23、24)
|
||||
|
||||
## C12 ElyMail(§12)
|
||||
|
||||
第二批发布(用户定,2026-10-08):首版不发布 ElyMail,首版的完整测试与打包不等它。
|
||||
|
||||
- [ ] C12.1 MailCore 2 构建、C 垫片、Rust 绑定,三类协议连通与 MIME 解析
|
||||
- [ ] C12.2 本地存储:SQLite 索引、内容目录、消息身份(账号、邮箱、UIDVALIDITY、UID)、容量(§12.3、§18.1)
|
||||
- [ ] C12.3 账号、TLS、OAuth 流程、应用密码(MAIL-01、02)
|
||||
- [ ] C12.4 同步:头与 90 天正文、按需附件、队列、POP3、同步控制(MAIL-14,§12.3)
|
||||
- [ ] C12.5 邮箱视图、阅读、会话线程(MAIL-03、04)
|
||||
- [ ] C12.6 写信、草稿、发送队列与发送状态机(MAIL-05、06、07,§12.4,QA-17)
|
||||
- [ ] C12.7 组织、搜索、附件(MAIL-08、09、10)
|
||||
- [ ] C12.8 规则、联系人、迁移、通知(MAIL-11、12、13、15,QA-16、18)
|
||||
|
||||
## R1 首版:六个应用的打包(§2.2、§21,用户 2026-10-08 定)
|
||||
|
||||
- [~] R1.1 八个应用的图标(同一套设计:macOS 为标准 squircle,Linux 与 Windows 为无底形);系统交给应用的文件(启动参数与访达的打开请求)都经同一条路打开;macOS 包自带各自的引擎(LOK 与其 worker、PDF worker 与 OCR 数据、ElyTree 的 Git),带文档类型与图标,按批次出 dmg — R1 FAIL 五项已修:正式签名的 CEF 辅助程序缺 JIT 许可,页面载入失败(渲染与 GPU 辅助程序带 `allow-jit`,`hardened-macos.sh` 以开发证书签名后页面画出);随包 Git 随本机 SDK 要求 macOS 27(以 14.0 构建,`release-macos.sh` 核对每个二进制的最低系统);同时打开多个 CSV 时前一个导入询问被替换丢失(排队逐个询问,单测);发行无许可与源码资料(每个包的 Licenses、源码包,缺即失败);图标测试放过全透明图标(核对标记与颜色)。另:嵌入整个 LibreOffice.app 使系统登记其快速查看插件,设置里出现“LibreOffice 扩展”(用户发现;改为只带内核并注销本机登记,测试不再运行 LibreOffice);Git 的内建命令改为链接(ElyTree 689 MB 降到 63 MB)。去掉各项时相应测试失败 — R2 FAIL 两项已修:MuPDF 编进 worker 的五组字体(urw、sil、noto、droid、han)缺许可(按构建实际嵌入的字体族收齐,`release-macos.sh` 核对);图标测试只验 ico 的 256 层(逐层、逐个 icns 尺寸验标记,清空 16 像素层即失败) — R3 FAIL 五项已修(不再送审):字体许可把 README 当作许可(每族须有 LICENSE、COPYING、NOTICE 或 OFL,README 只作附加;`release-macos.sh` 同样要求,去掉 SIL 的 OFL 即失败);ICO 小尺寸层换成实心方块仍通过(每个 png、ico 层与 icns 尺寸都与母版重新渲染逐像素比对,按预乘比较,macOS 的 16、32 像素旧格式容差 8;实心与清空 16 像素层都失败);三处注释超过十词(改短,`comments.py` 另查 packaging 下的 Python 与 Swift)
|
||||
- [ ] R1.2 各系统的文件关联(用户 2026-10-08 要求核对):`packaging/types.tsv` 一处定义;Linux 桌面项 `MimeType=`,Windows 安装程序“打开方式”注册与卸载(`packaging/windows/types.sh`,makensis 编过),macOS 照旧;都不设为默认;`tests/scripts/types.sh` 核对三端一致 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C16 应用之间的连接(§16)
|
||||
|
||||
- [x] C16.1 六个入口:邮件附件、导出查看、Markdown 版本、Agent 产物、文件作邮件附件、应用切换。首版做六个应用之间的三项(导出后查看、Markdown 查看版本、应用切换),邮件附件、文件作邮件附件与 Agent 产物随第二批 — R1 FAIL 三项已修:符号链接文件交给 ElyTree 时按目标文件取历史(只解析所在文件夹的真实路径,保留 Git 跟踪的名字,单测含仓库外的链接);系统默认程序打开的失败被吞(`apps::open_default` 核对退出状态并提示,单测);`mark-output.sh` 只按回车提交保存面板(改按其“存储”按钮,连跑两次通过)。另:系统打开、应用切换与 ElyMark 交 ElyTree 都改在后台运行,界面线程不等子进程 — R2 FAIL 一项已修:新加的符号链接单测未限 Unix,Windows 测试编译失败(加 `#[cfg(unix)]`;同类的 ElyDoc 导出、ElyMark 文件夹、ElyShow 图片三个旧单测一并加上);本机无 MSVC 工具链,Windows 测试编译未能本机核对
|
||||
|
||||
## C17 工程结构与接入边界(§17)
|
||||
|
||||
- [~] C17.1 进程与状态规则核对:工作进程、请求带会话与修订、FFI 边界、后台任务(§17.2、§17.4) — R1 FAIL 六项已修:LOK 文档号取内核指针、释放后被复用(每次载入一个会话号;关后再开四次,旧句柄被拒、新文档事件照常);PDF 请求不带预期修订(`Doc` 带历史标记,指向页与对象的请求在动内核前核对,过期以 `stale:` 拒绝;删页后按旧页码转页被拒);PDF 句柄不分 worker(句柄带 worker 会话,文档号带进程前缀);发送端可写超限帧(发送前即报错,worker 的超限结果回错误);LOK 输出读错时请求一直等(错误经回复通道交回,停下并回收 worker);PDF 打开中途失败泄漏原生文档(失败即释放,超过 MuPDF 256 MB 缓存后不再增长)。另:对话框关闭原以文档号当指针(回调另带原生句柄)。各项都有回归测试,去掉修复即失败。R2 FAIL 三项已修:LOK 按编号的命令不带预期修订(worker 记修订,`EXPECT` 核对,删表后按旧编号改名被拒);LOK 会话号每个 worker 都从 1 起(会话号带进程前缀,句柄带 worker 会话,别的 worker 的句柄被拒);PDF 附件的读取与删除带“任意”(改为带标记,同名附件换过后旧读取与旧删除都被拒、新附件不动)。随之:一次读取得出的一组命令只核对一次;不按键的鼠标移动不算改动;ElyTable、ElyShow 落后于视图所见修订时重读;读取闭包取视图的句柄。R3 FAIL 四项(第三轮后修,各有回归测试,去掉修复即失败):幻灯片备注的 `Part` 等编号不核对(编号参数补齐 `Part`、`TabNo`、行列与区域四角;删去前一张幻灯片后旧备注请求被拒、余下幻灯片的备注不动);重命名对话框确认时取最新修订(对话框记下打开时的修订,经 `run_on` 执行;对话框开着时删去第一张表,确认后 C 不被改名);输入失败时视图丢了 worker 已加的修订(失败也取回修订;拆分失败后输入器报出的修订与 worker 一致);ElyDoc 删除所示制表位被拒(属性栏按视图所见修订发;添加后删除再保存)
|
||||
|
||||
## C18 数据、配置与隐私(§18)
|
||||
|
||||
- [~] C18.1 配置:版本化、原子写入、文件锁、合并已知字段;通用与应用偏好 — 通用与应用的 `settings.json`(版本、锁、只改所写的键、原子替换),设置窗口(Cmd+,:外观、语言、动效、Omarchy、作者名、ElyTree 的 Git),别的应用写入即刻跟随,主窗口位置;顺带修 ElyMark 启动的应用收不到 SIGTERM、组件 `ThemeSelector` 文案与 `Banner` 无障碍角色 — R1 FAIL 五项已修:作者框只在开窗时读一次,别的应用改名后失焦写回旧名(未编辑时跟随,只写真正的编辑);Git 校验迟到覆盖后一次选择(提交带序号,校验后在同一把锁下核对是最新的才写,过期的不回显);写入失败的提示被随后的重读清掉(另记保存错误,以 Banner 显示);ElyTree 自己的设置读不出时只进日志(Git 页显示原因);版本核对只看 stdout(先核对退出状态、带上 stderr);各加回归(`settings.sh` 四个场景,去掉修复各自失败;`version_of` 单测) — R2 FAIL 两项已修:作者名写入前就当作已存,写失败后被别处的名字顶掉、重按回车也不再写(已存与键入分开,写成功才更新已存,失败的输入留着可重试);提交与磁盘相同的 Git 时不作废在途校验,慢校验随后写回旧程序(每次提交先作废在途校验);`settings.sh` 各加场景,去掉修复各自失败 — R3 FAIL 两项已修(不再送审):作者名的在途写入在等锁期间被改回原值后照样落盘(每次提交都编号,含改回已存值);Git 的“是否最新”在拿到文件锁之前核对,等锁期间被作废的写入锁释放后照写(`Store::set_if` 在文件锁下核对,作者与 Git 共用);回归:另一进程持锁时改回原值、最后的选择留下(`settings.sh` 两个场景,去掉锁下核对即失败),`set_if` 单测
|
||||
- [~] C18.2 凭据:系统凭据库,不可用时只留本次会话;日志不含正文与密钥 — 恢复密钥在凭据库不可用时只留本次运行(内存);worker 日志并入应用日志、两端都读 `ELY_LOG`;设置里的诊断先列内容、默认去掉路径与姓名再导出;`privacy.sh` 真窗口核对日志与诊断不含正文 — R1 FAIL 六项已修:引擎原生 stderr(MuPDF 警告引出内容流里的词)原样进日志(relay 只放行 worker 自己的结构化记录与崩溃栈帧,其余只计数;worker 的失败日志把引号里的片段换成 …;崩溃提示改为结构化记录);脱敏只换家目录(凡含 / 或 \ 的词与 URL 都换成 `<path>`,分段标题只写文件名);短名先换拆开长名(按长度从长到短);日志读不出被当作没有(只有不存在算没有,其余报错);relay 把 worker 的模块名改成 lok-worker 后按模块的 ELY_LOG 过滤失效(保留原模块名与级别);`privacy.sh` 清理时以 143 退出(保留自身状态);回归:真 worker 读坏内容流后日志无该词(放行原生行即失败)、脱敏与日志错误单测 — R2 FAIL 三项已修:引擎错误串经“去引号”仍漏出无引号的数据与字段内的引号(worker、ElyPDF 应用与 LOK 输入通道的失败日志只记请求号与错误类别,全文只经回复交给界面;去掉 `scrubbed`);带空格的路径只换掉第一段(从路径起点整段换掉:有引号到引号为止,否则到行末);单字姓名不换(凡非空姓名都换);回归:真 worker 拒绝带凭据的 ftp 地址后日志无该串(记错误全文即失败),脱敏单测含空格路径、Windows 路径与单字中文名 — R3 FAIL 四项已修(不再送审):双引号内的转义引号提前结束路径(双引号里反斜杠转义,按 JSON 找收尾);日志尾部从行中截断,半截路径的文件名漏出(截在行中即跳到下一整行);姓名来源读不出照样导出(`Redact::of_account` 返回错误,导出即失败并显示);LOK worker 启动失败用原生输出,原因被 relay 丢掉、客户端只见管道关闭(改为结构化记录,`Relay::last_error` 把最后一条错误带进客户端的报错);回归:各项单测,真 worker 装不上内核时报错带 worker 的原因(改回原生输出即失败)
|
||||
- [~] C18.3 数据管理:缓存、清理、恢复副本、导出;数据库升级前备份,失败只读 — 设置里的“存储”:本应用的日志、网页内容缓存(ElyMark)、恢复副本各自大小与清除(日志当场清空;缓存与恢复副本用着,记下后下次启动、打开任何东西之前清掉),每个文档的恢复点数与空间可选(写进应用设置,ElyMark 的恢复点照用;C06.5 的文档应用同);日志过 8 MB 时启动即只留后一半(从整行起);数据库升级与备份只属 ElyMail(第二批),首版六个应用无数据库;邮件与 Agent 历史导出同属第二批;`storage.sh` 真窗口核对;组件库 ea4ed4c:`SettingsRow` 是以标题命名、带说明的组,辅助技术说得出各控件设的是什么 — R1 FAIL 四项已修:恢复点数与空间两个选择各自写回读取时的整份,连续修改互相覆盖(以界面此刻的值改一项,提交带序号,在文件锁下核对最新);清除不经确认(一律先问,写明删什么、何时删,确认才清);启动时清除失败只进日志(设置页写明,记号留着下次再试);测试只核对名称(已知大小的样本核对显示的大小,选 10 后连写 12 个恢复点只留 10 个,确认前不删,持锁时两次选择都留下) — R2 FAIL 四项已修:页面切换后的回读盖掉了未写入的选择(选过未写下的限额留在页面上,写成之后才按磁盘);启动时清除失败仍打开数据(改为启动失败并说明原因,记号留着下次再试);`ax-values` 新增的说明列挡住了其后的坐标(说明移到坐标之前);日志与恢复副本的大小未核对(日志不小于样本,恢复副本等于磁盘上按同法算出的大小);测试另加持锁时切页再选、清除失败时不启动 — R3 FAIL 三项已修并加回归,三轮用尽:设置窗口关掉再开丢了待写入的限额(待写入的限额、提交序号与写入失败移到应用级的全局,不随窗口);清除成功盖掉了限额写入的失败(两者各记各的,限额的失败留到限额写入成功);一条注释超过十词;`storage.sh` 加关窗重开两次持锁选择都留下、限额写入失败后清空日志仍提示、再写成功后提示消失
|
||||
|
||||
## C19 文件兼容与输出(§19)
|
||||
|
||||
- [~] C19.1 版本化兼容清单、往返验证用例(独立于内核比较) — 已做:`docs/compatibility.md`(版本、内核与补丁、验证机,六个应用各自的编辑保存、导入受限、输出范围,已验证与限制);`independent.rs` 三个用例(DOCX、XLSX、PPTX:Ely 编辑、保存、关闭、再打开、再编辑、再保存),由 `tests/scripts/ooxml.py` 不经 LibreOffice 解 OOXML 检查,预期值由它自己算(整数、日期、错误值、文本严格,浮点按用例容差),DOCX 另交 macOS `textutil`。未做:与 Microsoft Office 的交叉检查(本机没有 Office);字体一致与缺失两种环境的对照 — R1 FAIL 六项已修:读取器改为按关系找部件(主文档、批注、设置、工作表、幻灯片顺序与各页备注,目标须在),批注须在其所在段落被引用,修订开关按 OOXML 的开关值读,表格另查输入格,删去无用函数;读取器另有自测(`ooxml.py selftest`:评审给的每种损坏都须被查出),往返用例在核对后跑它 — R2 FAIL 三项已修:批注须紧跟“Alpha 甲”(同段挪到别处即不过),幻灯片须各为其部件、按序为 First、空白、Second 且只有末张隐藏、备注只在首张;注释改为一行;三种损坏加入自测 — R3 FAIL 三项已修并加回归(各为一种自测损坏),三轮用尽:批注须仍是点(不带范围标记);第二次会话的 Gamma 也须是修订插入;备注须在备注页的正文占位符里(挪到页脚即不过)
|
||||
- [x] C19.2 打印与 PDF 输出:同一修订、纸张、页边距、缩放 — 已做:打印原用系统共用的打印设置(打印机默认纸张与边距),不缩放时 A4 以外的文档会被裁切或偏移;改为每次打印取一份副本,纸张取 PDF 首页的尺寸与方向、边距为零(文档的页边距已在 PDF 里);导出与打印都在该标签的输入落地之后(同一修订),对话框给出页数与范围,系统面板另有预览;取消不动文档,打印副本删去。`print.sh` 真窗口:A5 横向 DOCX(不经 LibreOffice 写成)以 595×420 点打印,取消后文件不变、打印副本不在、关闭不问。未验证:Windows 与 Linux 的打印(未构建) — R1 FAIL 三项已修:打印设置的副本另把缩放设为 1(原沿用系统共用的缩放);纸张取最大的一页(一次打印只有一种纸张,较小的页按原大印在其上,不裁不缩;逐页纸张做不到,已知限制);测试改核实际输出:`ELY_PRINT_TO` 让打印作业写成文件而不弹面板,`print.sh` 以 pdfinfo 与 pdftotext 读出页面尺寸、页数与文字位置(A5 页文字距左 72 点;A5 加 A3 的文档两页都在 A3 纸上、右缘文字在) — R2 FAIL 两项已修:纸张按两轴取(最长边与各页短边的最大者,方向随最大的一页,各页按自身旋转计),宽高交叉的页也不裁(`paper_for` 单测;`print.sh` 加 1000×200 与 600×600 混排,两端文字都在);`ELY_PRINT_SHARED_SCALE` 让测试先把共用缩放设为一半,A5 文字仍距左 72 点才算过(去掉缩放复位即失败) — R3 PASS
|
||||
|
||||
## C20 质量与性能(§20)
|
||||
|
||||
- [~] C20.1 性能基准:PERF-01 至 PERF-09 的度量工具与记录格式 — 已做:`tests/perf/bench.py`、`samples.py`、`docs/perf/README.md`;办公三应用先开窗口、内核在后台启动(ElyDoc 日常启动 P95 由 3.9 秒降到 0.19 秒,空白文档仍随内核约 2.7 秒);PERF-08 跑 40 次,内存起落、文件数不变;PERF-09 属第二批。未达标照实记录:首次按键到图块的 P95 约 250 毫秒(内核首次输入的初始化)、空白文档就绪 P95 超 3 秒;2026-10-08 全量重测八项全部达标(补丁 0039、PERF-02 计时、关闭标签释放图像,见 agents.md) — R1 FAIL 六项已修:worker 启动核对失败后保持失败、一切请求被拒(单测);PERF-03、04 不再以缓存或请求日志为成功;DOCX 样本实为 62 页(分页符改放在标题段内,测前核对 100 页);PERF-02 每键须有计时;PERF-08 记基线与内核文档数;注释超长 — R2 FAIL 四项已修:首屏改为图块真正画到窗口上之后(`lok: first tile drawn`、`pdf: first tile drawn`,ElyPDF 的图块改由画布直接画以知成败),窗口画失败亦作废;PERF-08 按句柄身份(类型与名称)比对基线,多出的只许 `/System/Library/` 的只读资源且每次相同,首次关闭漏下 `/dev/null` 即失败;文档数日志缺失即失败;`tests/perf/test_bench.py` 以造出的日志与句柄核对这些判定;按修后的基准重测记录 — R3 FAIL 三项已修(不再送审):PERF-02 计时改在显示该编辑的图块真正画到窗口后结束(`EditClock::drawn`,单测),打字时有图块画失败即失败;PERF-08 每轮只认本轮写下的文档数(日志游标,单测);删去不再用的 `files()`;PERF-08 的 Cmd+N、Cmd+W 改以带 Command 标志的事件直接投给应用(`chord.swift`),原 osascript 偶尔丢掉 Command 打出 w
|
||||
- [ ] C20.2 稳定性:保存失败、内核崩溃、磁盘不足、只读目录、休眠 — 已做:写满磁盘的保存(4 MB 磁盘映像,原文件不变、不留半成品);`tests/perf/soak.py` 长时编辑切换(两个文档轮流键入、保存、只在一个里撤销,每 10 轮以不经内核的读取器核对:内容在、撤销不跨文档、文件不坏);它查出图像不释放(见 agents.md),修后 40 分钟 673 轮全部核对通过,内存 359 至 468 MB(文档与撤销历史随之增长,约每轮 0.17 MB);内核崩溃与强制结束见 `recovery.sh`、`pdf-recovery.sh`。未做:8 小时整跑、系统休眠(测试不能让本机休眠)。2026-10-08 01:34 ElyMark 在 CEF 内一次 XPC 回复释放处 SIGTRAP(`~/Library/Logs/DiagnosticReports/ely-mark-2026-10-08-013420.ips`,CEF 帧无符号),未复现,信息不足 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
- [ ] C20.3 无障碍与国际化复核 — 已做:`docs/accessibility.md`(§20.3 逐条:依据与缺口);查出文档区与 ElyMark 页面把 Tab 留给自己、键盘无出口,组件库 029632b 加 F6 与 Shift+F6 照 Tab 次序移出移回(`f6_leaves_a_field_that_keeps_tab`);内核数字区域固定 en-US,改界面语言不重解释已存数值 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C21 开源与许可证(§21)
|
||||
|
||||
- [ ] C21.1 AGPL 许可文件、第三方通知、锁定依赖清单、源码对应 — 已做:`docs/licenses.md`;`release.sh` 另出上游源码包(实际编译的打过补丁的 MuPDF 与随包 Git),`release-macos.sh` 核对其中为打过补丁的 MuPDF。未做:法律审查;LibreOffice 外部库的源码随包与否 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C22 测试与验收(§22)
|
||||
|
||||
- [ ] C22.1 功能编号到测试用例的对照表 — 已做:`scripts/test-map.py` 生成 `docs/test-map.md`(首版 159 个编号各有任务,写明的测试与测试所在) — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
- [ ] C22.2 QA-01 至 QA-28 的执行记录 — 已做:`docs/qa.md`;六个发行应用空闲时无网络套接字(`lsof -i`) — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C23 工程风险证据(§23)
|
||||
|
||||
- [ ] C23.1 九项风险的证据与处理 — 已做:`docs/engineering-risks.md` 末尾 PRD §23 九项的证据与状态 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
|
||||
## C24 交付物与完成定义(§24)
|
||||
|
||||
- [ ] C24.1 交付物清单、构建说明、已知限制、测试报告 — 已做:根目录 `README.md`(构建、测试、文档索引)、`docs/help.md`(打开保存、恢复、离线边界、仓库操作);已知限制与测试报告见 `docs/compatibility.md`、`docs/qa.md`、`docs/perf/` — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
- [ ] C24.2 验收逐条核对表 — 已做:`docs/acceptance.md`;结论:macOS 首版六个应用可交付,PRD 的首个正式版(八个应用、三端)未达到 — 未送审:用户 2026-10-09 定性能已够、先发首版
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
scripts/doctor.sh:14 — The version command's exit status is thrown away. Only `command -v` decides whether a tool is missing, so a tool that is on PATH but cannot run counts as present. Its error text is printed as if it were the version.
|
||||
Scenario (reproduced): put a `cmake` on PATH that prints `dyld: Library not loaded: libcurl.4.dylib` and exits 134. doctor prints `cmake dyld: Library not loaded: libcurl.4.dylib` and exits 0. Same result with `RUSTUP_TOOLCHAIN=nonexistent-xyz`: rustc and cargo print `error: override toolchain ... is not installed` and doctor still exits 0. This breaks "缺项以非零码退出".
|
||||
Fix: capture the output and exit status of `"$tool" "$@"`. If the status is non-zero, print the tool as failed with its error and set missing=1.
|
||||
|
||||
scripts/doctor.sh:2 — The comment has 12 words (`Build tools Ely Office needs. Prints versions. Exits 1 on any miss.`). agents.md rule 5 caps comments at ten words.
|
||||
Scenario: any rule check of comment length flags this line.
|
||||
Fix: cut it to ten words or fewer, or remove it.
|
||||
|
||||
scripts/doctor.sh — No test covers the non-zero exit, which is the item's acceptance criterion. Nothing in the repo runs doctor with a missing or broken tool and asserts exit 1. The fact that the line-14 defect went undetected shows this gap.
|
||||
Scenario: a change that makes the script always `exit 0`, or that drops a tool from the list, passes unnoticed.
|
||||
Fix: add a test that runs doctor.sh with a stub PATH. Cover (a) one required tool absent and (b) one tool that exits non-zero. Assert exit 1 and the matching report line in each case, and assert exit 0 when every tool is present and works.
|
||||
@@ -0,0 +1,14 @@
|
||||
FAIL
|
||||
|
||||
r1 check:
|
||||
- doctor.sh:12 broken tools: fixed. With `RUSTUP_TOOLCHAIN=nonexistent-xyz`, doctor prints `BROKEN rustc: ...` and `BROKEN cargo: ...` and exits 1.
|
||||
- doctor.sh:2 comment length: fixed. It is now nine words.
|
||||
- missing exit-code test: partly fixed. tests/scripts/doctor.sh covers all-present, absent, and broken, and it passes locally. The r1 scenario "drops a tool from the list, passes unnoticed" still holds; see the first entry below.
|
||||
|
||||
tests/scripts/doctor.sh:15 — The test builds its stub list from the `check` lines in doctor.sh, then asserts output only for cmake and ninja. If a required tool is removed from doctor.sh, it also disappears from the test's expectations. C00.1 names cmake, ninja, pkg-config, and git-lfs, but nothing pins pkg-config or git-lfs.
|
||||
Scenario (reproduced): delete the `check pkg-config` and `check git-lfs` lines from a copy of doctor.sh and run the test against it. It prints `doctor.sh: 3 cases pass` and exits 0.
|
||||
Fix: hard-code the required tool list in the test. In the all-present case, assert that a version line appears for each of those tools.
|
||||
|
||||
tests/scripts/doctor.sh:13 — The test sets `PATH` to `$stubs:/usr/bin:/bin`, so in the "absent" case doctor still finds any real tool installed in a system directory. On Linux, `apt install cmake` puts it at /usr/bin/cmake. Removing the cmake stub then exposes the real cmake, doctor exits 0, and the test fails. Whether the test passes depends on the host, including the Linux and Raspberry Pi targets.
|
||||
Scenario (reproduced): add a directory holding a real `cmake` to the test's PATH after the stubs, standing in for /usr/bin on Linux. The test prints `FAIL: cmake absent: doctor exited 0` and exits 1.
|
||||
Fix: give doctor a PATH that contains only the stub directory. Start doctor with an absolute bash path so the `env bash` lookup is not needed, and either stub or link `head` in the stub directory or replace `head` with parameter expansion in doctor.sh.
|
||||
@@ -0,0 +1,13 @@
|
||||
PASS
|
||||
|
||||
r2 check:
|
||||
- tests/scripts/doctor.sh:15 test list tracked doctor's own list: fixed. The test now hard-codes the 11 tools on line 8 and asserts a version line for each one. Re-ran the r2 scenario by deleting `check pkg-config` and `check git-lfs` from a copy of doctor.sh. The test prints `FAIL: no version line for pkg-config` and exits 1.
|
||||
- tests/scripts/doctor.sh:13 real system tools leaked into the absent case: fixed. Line 14 sets `PATH="$stubs"` only and starts doctor with `"$BASH"`. doctor.sh uses only builtins now, since `head` was replaced by `${out%%$'\n'*}`, so nothing outside the stub directory can be found.
|
||||
|
||||
Verified this round:
|
||||
- `bash tests/scripts/doctor.sh` prints `doctor.sh: present, absent x11, broken pass` and exits 0. It does the same under /bin/bash 3.2.
|
||||
- `bash scripts/doctor.sh` on this host prints versions for all 11 tools, including cmake 4.4.3, ninja 1.13.2, pkg-config 3.0.7 and git-lfs 3.8.0, and exits 0.
|
||||
- Mutant with `exit 0` in place of `exit "$failed"`: the test fails with `rustc absent: doctor exited 0`.
|
||||
- A stub on PATH that is not executable is reported as `BROKEN cmake: ... Permission denied`, and doctor exits 1.
|
||||
|
||||
No new defects in the changed code.
|
||||
@@ -0,0 +1,15 @@
|
||||
FAIL
|
||||
|
||||
Ran `bash tests/scripts/references.sh`: exit 0, "14 pins match". All 14 `reference/*` HEADs equal their pins and every worktree is clean. The defects below are gaps that this run cannot catch.
|
||||
|
||||
scripts/fetch-references.sh:12 — The "already done" check compares only HEAD against the pin. When HEAD matches, the function returns before lines 19–21, so a changed sparse path list is never applied. The script prints `ok` and exits 0 with sources missing.
|
||||
Scenario (reproduced on a scratch copy of reference/libreoffice-core): add `vcl/inc` to the libreoffice-core sparse list and run the script. It prints `ok libreoffice-core 8221e31…` and exits 0. `reference/libreoffice-core/vcl` does not exist, and `sparse-checkout list` still shows the old five paths. tests/scripts/references.sh also passes, because it only probes `LibreOfficeKit.h`.
|
||||
Fix: make the skip condition also require that the recorded sparse set equals the requested paths. For example, compare `git sparse-checkout list` with "$@". Otherwise re-apply `sparse-checkout set` on every run.
|
||||
|
||||
scripts/fetch-references.sh:38 — mailcore2's pinned dependencies are never checked out. `deps/ctemplate`, `deps/libetpan` and `deps/tidy-html5` are submodules of the pin, but only mupdf gets `submodule update` (line 36). `git -C reference/mailcore2 submodule status` lists all three with `-` (uninitialized), and `deps/libetpan` is an empty directory. This breaks "全部 pin 检出". It also leaves C00.4 ("MailCore 2 依赖可构建") with no pinned libetpan, ctemplate or tidy sources: build-mac and build-android consume `deps/`.
|
||||
Scenario: run the script on a fresh machine, then try to build MailCore 2 from `reference/mailcore2` following build-mac/README.md. The libetpan sources are absent, and the script and test both report success.
|
||||
Fix: run `git -C "$ref/mailcore2" submodule update --init --depth 1 --recursive`, the same way as mupdf. Have the test assert this.
|
||||
|
||||
tests/scripts/references.sh:25 — Submodule state is checked through a single file, `mupdf/thirdparty/freetype/.../freetype.h`. The other 17 mupdf submodules (harfbuzz, lcms2, jbig2dec, openjpeg, libjpeg, zlib, …) are never checked for being initialized or for being at the commit the mupdf pin records.
|
||||
Scenario: `git -C reference/mupdf submodule deinit -f thirdparty/harfbuzz`, or a submodule left at another commit (`+` in `submodule status`). The test still prints "14 pins match" and exits 0, while C00.4's MuPDF build fails.
|
||||
Fix: for each repo whose submodules the script fetches (mupdf, and mailcore2 after the fix above), fail when any line of `git submodule status --recursive` starts with `-` or `+`.
|
||||
@@ -0,0 +1,13 @@
|
||||
PASS
|
||||
|
||||
Ran `bash scripts/fetch-references.sh`: exit 0, all 14 print `ok`. Ran `bash tests/scripts/references.sh`: exit 0, "14 pins match, sparse set and submodules in place, gpui rev agrees". Every `reference/*` worktree is clean.
|
||||
|
||||
r1 defects, each confirmed fixed:
|
||||
|
||||
scripts/fetch-references.sh:18 — The skip now also requires `sparse_ok`. Reproduced the r1 scenario on a scratch copy of reference/libreoffice-core with `vcl/inc` added to the list. The script printed `fetch`, `sparse-checkout list` showed six paths, and `vcl/` was present. A second run printed `ok`.
|
||||
|
||||
scripts/fetch-references.sh:45 — mailcore2 submodules are now initialized. In reference/, ctemplate, libetpan and tidy-html5 show a leading space in `submodule status --recursive`, and `deps/libetpan` has sources. A fresh scratch fetch of mailcore2 checked out all three at their recorded commits. Deinit of `deps/tidy-html5` showed `-`. The next run restored it.
|
||||
|
||||
tests/scripts/references.sh:39 — The test now fails on any `-`, `+` or `U` line from `submodule status --recursive`, or on an empty list, for mupdf and mailcore2. Confirmed that the grep catches a deinitialized submodule.
|
||||
|
||||
No new defects in the changed code.
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
Ran `bash tests/scripts/agents-md.sh`: exit 0, "agents.md: 115 component names exist". The component library is at b54500b. Every brace-listed name in the map is re-exported from its module's `mod.rs`, or for `theme::{Theme, Mode}`, defined in `theme/mod.rs`. `Theme::set_palette` exists at `theme/mod.rs:163`. The gap claims hold. `documents/viewer.rs:290` maps every page into the element tree with no culling, which breaks PERF-05 ("不先渲染全部页面"). `FixedFormatToolbar` holds an `Entity<TextInput>` (`documents/toolbar.rs:82`). `misc::WebView` is a native macOS overlay, not a CEF off-screen view. No formula bar or name box is exported from `tables`.
|
||||
|
||||
tests/scripts/agents-md.sh:28 / agents.md:34 — agents.md says the script "核对下表的每个名字", but the script only extracts `module::{...}` brace groups. It does not check `editor::FindWidget` on the Doc row (agents.md:45) or `Theme::set_palette` (agents.md:52). Scenario: on a scratch copy, changing line 45 to `editor::NoSuchWidget` or line 52 to `Theme::no_such_fn` still gives exit 0 and "115 component names exist". Fix: make the script also extract the bare `module::Name` and `Type::method` forms in the map, or write every map entry in brace form and stop naming items the script cannot check.
|
||||
|
||||
tests/scripts/agents-md.sh:21 — The script greps for `pub (struct|enum|fn|trait|type) Name` anywhere under the module directory. It does not check that `module::Name` resolves. This passes items in private submodules and methods inside `impl` blocks. Scenario: on a scratch copy, adding `documents::{..., Mark}` and `git::{new, ...}` gives exit 0 and "117 component names exist". But `documents::Mark` sits in private `mod markdown` and is not re-exported, and `git::new` is only a method (`git/review.rs:69`). `use ely_gpui_component::documents::Mark` would not compile. Fix: check each name against the module's public surface, meaning the `pub use` lists in `mod.rs` plus items defined in `mod.rs` itself. Better still, compile a generated `use ely_gpui_component::module::{...};` file against the pinned crate.
|
||||
|
||||
tests/scripts/agents-md.sh:2 — The comment is eleven words, over the ten-word limit. It also says "is defined", which is the weak property the script checks, not the import path the map promises. Fix: shorten it to ten words or fewer, and state the property the corrected check verifies.
|
||||
|
||||
agents.md:34 — TASKS.md C00.3 asks for the gpui rules to be "写入 `agents.md`". agents.md only says "gpui 用法规则见该仓库 `AGENTS.md`". That points into `reference/`, which agents.md:27 marks as not part of this project. The upstream file is 254 lines and mixes library-internal notes (gallery capture harness, wasm build, website) with the rules a consumer must follow. Examples of the latter: `ely_gpui_component::init` panics unless `Assets` is passed to `with_assets`; the root goes in a `FocusScope`; a state change outside render calls `cx.notify()` and, during paint, `request_animation_frame`; overlays go through `primitives::raise`; `uniform_list` rows set `w_full`. Scenario: an implementer of C01.1 reads only agents.md, as the project rules say to, and gets none of these. Fix: write the consumer-facing gpui rules into agents.md, or change the C00.3 line in TASKS.md if delegating to upstream is the decision.
|
||||
@@ -0,0 +1,19 @@
|
||||
FAIL
|
||||
|
||||
Ran `python3 tests/scripts/agents_md.py`: exit 0, "agents.md: 116 component names are public". The component library is at b54500b.
|
||||
|
||||
Round 1 defects:
|
||||
- The script missed bare names. Fixed. Every map entry is now in brace form: `editor::{FindWidget}`, and `Theme::set_palette` was removed. Changing the map to `editor::{NoSuchWidget}` now exits 1.
|
||||
- The script grepped the whole module tree. Partly fixed. It now reads only `mod.rs`, so `git::new` and `documents::Mark` are rejected. Methods in `impl` blocks inside `mod.rs` still pass. See the first entry below.
|
||||
- The script comment was too long. Fixed. The docstring is now nine words.
|
||||
- The gpui rules were missing. Fixed. agents.md:66-79 holds the consumer rules. I checked each one against upstream `AGENTS.md`, `src/lib.rs:54-59` and `examples/docs/hello.rs:40-44`. `motion::duration`, `primitives::raise`, `FocusScope::root`, `i18n::I18n`, `Theme::set_mode` and `Theme::set_mode_now` all exist.
|
||||
|
||||
The gap claims still hold. `tables::Spreadsheet`, `documents::FixedFormatToolbar` and `documents::DocumentViewer` are exported. No formula bar or name box exists in `src`. PERF-05 is at PRD.md:732.
|
||||
|
||||
tests/scripts/agents_md.py:9 — The `ITEM` regex is `^\s*pub\s+(?:struct|enum|fn|...)` and allows leading whitespace. It therefore collects `pub fn` methods inside `impl` blocks in `mod.rs` as module items. Scenario: on a scratch copy, changing agents.md:52 to `theme::{Theme, Mode, set_mode_now, set_palette}` gives exit 0 and "118 component names are public". Both names are associated functions of `impl Theme` (`theme/mod.rs:140`, `:163`), so `use ely_gpui_component::theme::set_palette` does not compile. This is round 1's "methods inside `impl` blocks" scenario, still open. Fix: count only top-level items (no indentation, outside any `impl` or inline `mod`). Better: compile a generated `use ely_gpui_component::module::{...};` file against the pinned crate.
|
||||
|
||||
tests/scripts/agents_md.py:26 — A glob re-export yields the literal name `*`. Names it brings in are then reported as not public. Scenario: `motion/mod.rs:18` has `pub use curve::*;`, which exports `motion::duration`, the function agents.md:77 tells apps to use. Adding `motion::{duration, ...}` to the map gives "FAIL: motion::duration is not public", which is false. Fix: the compile check above handles this too. Otherwise resolve `name::*` by reading that submodule's top-level `pub` items.
|
||||
|
||||
agents.md:100 — The documented command for script tests is `tests/scripts/*.sh`. The new check is `agents_md.py` (renamed from `agents-md.sh` in round 1), so that glob no longer runs it. Scenario: `for t in tests/scripts/*.sh; do bash "$t"; done` runs only `doctor.sh` and `references.sh`. A map name that drifted from the library goes unnoticed. Fix: list `tests/scripts/agents_md.py` in the commands section, or make the documented glob cover it.
|
||||
|
||||
agents.md:68 / agents.md:73 — The section says its rules are "取自组件库 `AGENTS.md`". Upstream `AGENTS.md` contains no rule "不用 `request_animation_frame` 轮询布局". Its own inspector requests a frame on each render while picking (upstream AGENTS.md:229). The startup chain and the `Assets` panic come from `src/lib.rs:57` and the examples, not from `AGENTS.md`. Scenario: someone checking the source of the polling ban finds nothing upstream that backs it. Fix: name the real source of each rule. If the polling ban is this project's own rule (it follows from rule 9), state it as that.
|
||||
@@ -0,0 +1,17 @@
|
||||
PASS
|
||||
|
||||
I ran `python3 tests/scripts/agents_md.py`. It exited 0 and printed "agents.md: 117 component names are public". The component library is at b54500b.
|
||||
|
||||
Round 2 defects:
|
||||
- Methods in `impl` blocks counted as module items. Fixed. `ITEM` is now anchored at column 0 (`agents_md.py:9`). On a scratch copy, `theme::{Theme, Mode, set_mode_now, set_palette}` exits 1 with "theme::set_mode_now is not public".
|
||||
- Glob re-exports. Fixed. `glob_names` reads the top-level `pub` items of the submodule. `motion::{duration, ...}` is now in the map (agents.md:51) and passes. `lerp` and `spring` from `curve::*` also pass, which is correct. `NoSuch`, `curve`, `self`, an empty name and `{}` each exit 1. An unknown module also exits 1, and so does an unresolvable glob.
|
||||
- Script tests missing from the commands. Fixed. agents.md:100 now lists `*.py`.
|
||||
- Source of the gpui rules. Fixed. agents.md:68 now names `src/lib.rs` and `examples/docs/hello.rs` as well. The polling ban is in upstream `AGENTS.md:116` ("Never poll layout with `request_animation_frame`"), so the round 2 claim that it had no upstream source was wrong.
|
||||
|
||||
Rechecked:
|
||||
- agents.md:70-79. Each rule matches an upstream source: `AGENTS.md` lines 45-47, 51-52, 56, 60, 62, 75, 116, 133, 162, 188 and 218; `src/lib.rs:57`; `hello.rs:16-17` and `:42`.
|
||||
- Every mapped module is `pub mod` in `src/lib.rs`.
|
||||
- The gap claims hold. `DocumentViewer` maps every page (`documents/viewer.rs:290`). `FixedFormatToolbar` takes an `Entity<TextInput>`. `tables::Spreadsheet` is exported. `src` has no formula bar and no name box. PERF-05 is at PRD.md:732.
|
||||
- File sizes are within the limit: agents.md has 108 lines and agents_md.py has 48. The only comment in the script is a nine-word docstring.
|
||||
|
||||
No defects found.
|
||||
@@ -0,0 +1,29 @@
|
||||
FAIL
|
||||
|
||||
What I ran and confirmed:
|
||||
|
||||
- I mounted `engines/LibreOffice_26.2.6_MacOS_aarch64.dmg` (297 798 926 bytes). `nm -gU` shows `libreofficekit_hook`, `libreofficekit_hook_2` and `lok_preinit`. The only VCL plugin is `libvclplug_osxlo.dylib`.
|
||||
- I rebuilt the three LOK spikes against `reference/libreoffice-core/include` and ran them on a fresh profile:
|
||||
- Threaded mode dies with `NSWindow should only be instantiated on the main thread!`.
|
||||
- Unipoll with runLoop never calls the poll callback.
|
||||
- Unipoll with GCD prints "polls so far: 0". It then gives BuildId 8221e31, a size of 12808 × 16408, an all-zero tile and a PDF.
|
||||
- `init.cxx:4277/4348` and `configure.ac:5950-5952` match the doc.
|
||||
- The patch is byte-identical to the `engines/lo-src` working-tree diff. It applies cleanly to the pinned `init.cxx`. The LO build is still running, so the patch has not yet been shown to compile; the doc defers that to C07.1.
|
||||
- MuPDF: the lib sizes match. The rebuilt `mupdf_spike` on the LOK PDF gives 1 page, 612x792, 1224x1584, 8241 dark pixels, and the CJK text.
|
||||
- `libmupdf-pkcs7.a` imports OpenSSL EVP symbols.
|
||||
- CEF: the index sizes match, and the macosarm64 URL answers 200 with 132224904 bytes.
|
||||
- The tessdata sizes match.
|
||||
- libetpan 6dc099a fails on `md5.c` with default flags and builds with `CFLAGS=-std=gnu17`.
|
||||
- `tests/scripts/*.sh` and `agents_md.py` exit 0.
|
||||
|
||||
docs/engineering-risks.md:22 — The doc says upstream master's `init.cxx` has "没有出图块的支持". That is false. Master contains bd2d0bec67, "coda-m: Don't skip the tile painting on macOS" (committed 2025-12-10). It changes the exact guard the doc cites, from `defined(UNX) && !defined(MACOSX)` to `defined(UNX)`. That is the same change as the patch's second hunk.
|
||||
Scenario: someone checks upstream before C07.1 and finds the doc wrong. Nothing in the doc says why a backport is not enough. With that commit, macOS takes the `#else` branch, which uses `SetOutputSizePixelScaleOffsetAndLOKBuffer`. `AquaSalVirtualDevice` does not override `SetSizeUsingBuffer`, in either the pin or master. So upstream's path still ignores `pBuffer` under Aqua, and that is the real reason the CoreGraphics hunk is needed. The doc never states this.
|
||||
Fix: cite bd2d0bec67, say what it fixes and what it does not, and base the need for the patch on the Aqua `SetSizeUsingBuffer` fact.
|
||||
|
||||
docs/engineering-risks.md:38 and :42 — R-03 does not record a blocker. The locked MailCore 2 (1b48bab) includes `<libetpan/mailgmail.h>` and `<libetpan/mailactivesync.h>`. Neither the pinned submodule libetpan 6dc099a (2022-06-27) nor Homebrew's libetpan 1.10.1 has them. Point 2 blames the gap only on the prebuilt libetpan-osx-21.
|
||||
Scenario: I built and installed 6dc099a with `-std=gnu17`. `inst/include/libetpan` has no `mailgmail.h` and no `mailactivesync.h`. The conclusion's plan ("用 `cc` 构建、固定源文件清单,对 ICU 头文件打补丁") would fail on `MCGmailSession.cpp` and `MCActiveSyncSession.cpp` in C12.1, just as the CMake attempt did. "锁定的 libetpan 可构建" (line 9) proves only that the wrong version of the dependency builds. That does not meet the C00.4 requirement "MailCore 2 依赖可构建".
|
||||
Fix: state that the pinned submodule is too old for the pinned MailCore 2. Upstream libetpan master has `src/low-level/gmail/mailgmail.h` and `src/low-level/activesync/mailactivesync.h`. Pin a libetpan commit that has these APIs and show that it builds, or record the gap as a blocker and update the status row and the conclusion.
|
||||
|
||||
docs/engineering-risks.md:21, :31 — The evidence for R-01 facts 3–5 and all of R-02 is spike programs and a sample `.docx`. They exist only in another session's `/private/tmp/.../scratchpad/spikes`. The repo does not contain them, and the doc names no source or command except the `nm` check in fact 1. agents.md's 2026-10-01 decision says "证据见 R-01", and PRD §23 asks for submitted evidence.
|
||||
Scenario: once the temp directory is purged, no one can re-run the threaded crash, the unipoll/GCD dispatch, the zero tile or the MuPDF render. Also, the spikes pre-zero the tile buffer with `calloc`, so "返回全零缓冲" cannot tell "wrote zeros" apart from "never wrote".
|
||||
Fix: commit the spike sources and the sample document with their run commands, for example under `tests/scripts` with a check that skips when `engines/` is absent and says so. Fill the tile buffer with a sentinel before `paintTile`.
|
||||
@@ -0,0 +1,52 @@
|
||||
FAIL
|
||||
|
||||
Round-1 defects:
|
||||
|
||||
- R-01 upstream claim: fixed. Line 22 cites bd2d0bec67. At the pin, `vcl/quartz/salvd.cxx` has no `SetSizeUsingBuffer` override; only the base in `vcl/inc/salvd.hxx:46` exists.
|
||||
- R-03 libetpan blocker: fixed. libetpan is now pinned at 4d47862. I built it from scratch in a scratch directory and it builds and installs both headers. All 76 `mailgmail_*`/`mailactivesync_*` functions that MailCore 2 calls are defined in `libetpan.a`.
|
||||
- Spikes not in the repo: fixed. `tests/engines/lok_probe.c` and `mupdf_probe.c` are in the repo, the sample documents are generated, and the tile is pre-filled with 0xAB.
|
||||
|
||||
What I ran:
|
||||
|
||||
- `tests/engines/lok.sh` exits 1 twice in a row. See the first entry.
|
||||
- `tests/engines/mupdf.sh` passes. The library sizes match, and the pkcs7 archive imports 17 EVP/PKCS7 symbols.
|
||||
- `tests/engines/libetpan.sh` passes.
|
||||
- `tests/scripts/*.sh` and `agents_md.py` exit 0.
|
||||
- The patch is byte-identical to the `engines/lo-src` diff. It applies with `git apply --check` to the pin.
|
||||
- The `init.cxx:4277/4348` and `configure.ac:5950-5952` references match.
|
||||
|
||||
tests/engines/lok.sh:12 — The cleanup runs `hdiutil detach "$mount" -quiet || true`. The detach fails with "Resource busy", and `|| true` swallows the error. `rm -rf "$work"` then walks into the still-mounted read-only dmg.
|
||||
Scenario: on this machine, two runs in a row printed "lok: load, PDF export, thread rule pass; tile untouched". Then came about 3 MB of `rm: ... Read-only file system` lines, and the script exited 1. The dmg stayed mounted under `/var/folders/.../tmp.*/mnt` until I detached it by hand a few seconds later. The evidence script for R-01 never exits 0, and every run leaks a mount.
|
||||
Fix: make cleanup wait until the image is released, for example by retrying the detach until it succeeds or a deadline passes. A detach failure must fail the script. Never run `rm -rf` over a path that is still mounted.
|
||||
|
||||
docs/engineering-risks.md:25 (status row :7) — The doc says "构建关闭 Skia" and "构建已在 `engines/lo-src` 进行". Neither is true. No build process is running, and `engines/lo-build.log` (17:34) ends in `make: *** [Makefile:302: build] Error 2`. The cause is six errors in `vcl/osx/salframeview.mm:802-847` (`SkiaHelper` and `CAMetalLayer` undeclared). That code is not guarded by `HAVE_FEATURE_SKIA` and asserts `"macos requires skia"`. `config_host.mk` shows `ENABLE_SKIA=` because `--disable-skia` is in `autogen.lastrun`.
|
||||
Scenario: C07.1 follows the documented plan of a self-build with Skia off, and it cannot compile LibreOffice 26.2.6.3 on macOS. With Skia on (which is required), the patch's reasoning about the Quartz `AquaSalGraphics` drawing into a foreign `CGContext` has not been examined. C00.4 requires blockers to be written down; this one is missing. The agents.md decision depends on it too.
|
||||
Fix: record the failed Skia-off build as a blocker, citing the log and `salframeview.mm:802`. Correct the build status. Either show that a Skia-on build still sends the patched `paintTile` path through Quartz into `pBuffer`, or record that as unverified and change the R-01 row.
|
||||
|
||||
docs/engineering-risks.md:42; scripts/build-libetpan.sh:21 — The doc says `md5.c` fails to compile under the default C standard because of K&R definitions. At the pinned 4d47862, `md5.c` uses ANSI prototypes (`MD5Init(MD5_CTX * context)` at :92). A clean `./autogen.sh` with no `CFLAGS`, run with Apple clang 21, builds and installs with no errors. Setting `CFLAGS="-std=gnu17"` replaces autoconf's default `-g -O2`, so the generated Makefiles have `CFLAGS = -std=gnu17 -W -Wall`.
|
||||
Scenario: the shipped libetpan is built at -O0 with no debug info, to work around a failure that does not happen at this pin. The doc records a false reason.
|
||||
Fix: drop the `CFLAGS` override, or keep the default optimization flags. Delete the `md5.c` sentence, or confine it to 6dc099a.
|
||||
|
||||
scripts/build-libetpan.sh:16, :21-22 — These lines are silent fallbacks.
|
||||
- `brew --prefix openssl@3 ... || echo /usr` points OpenSSL at `/usr`, which has no OpenSSL headers on macOS. libetpan's `configure.ac:227-243` then sets `with_openssl=no` without an error.
|
||||
- json-c is auto-detected (`configure.ac:841-852`, `with_json=auto`). Without json-c ≥ 0.14, the `gmail` subdirectory and `mailgmail.h` are left out (`src/low-level/Makefile.am:40-46`). This build found Homebrew json-c 0.19.
|
||||
The doc's dependency list (:42) does not mention json-c.
|
||||
Scenario: on a machine without Homebrew or with no `OPENSSL_DIR`, libetpan builds without TLS. `tests/engines/libetpan.sh` only checks headers, so it still passes, and PRD §23 requires TLS for MailCore 2. On a machine without json-c, the test fails with "pinned libetpan lacks libetpan/mailgmail.h", which hides the real cause.
|
||||
Fix: require `OPENSSL_DIR` or the brew prefix and fail if it is missing. Pass `--with-json`, so configure errors out when json-c is absent. Assert `HAVE_OPENSSL` in the generated `config.h` in the test. List json-c in R-03.
|
||||
|
||||
tests/engines/mupdf.sh:44 — The only check on the signing helper is `[ -s libmupdf-pkcs7.a ]`. When `pkg-config libcrypto` fails, MuPDF's `Makerules:238` leaves `HAVE_LIBCRYPTO` unset. `pkcs7-openssl.c:28` then compiles stub versions of the same three symbols, and the archive is still non-empty.
|
||||
Scenario: on a machine without OpenSSL's pkg-config file, the test prints "signing helper pass". The R-02 claim "用 OpenSSL 3.6.4 构建成功" is false there.
|
||||
Fix: pass `HAVE_LIBCRYPTO=yes` in `scripts/build-mupdf.sh`, and assert that `nm -u libmupdf-pkcs7.a` lists EVP/PKCS7 symbols.
|
||||
|
||||
docs/engineering-risks.md:16 — The doc says `lok.sh` asserts each fact listed below ("逐条断言下列事实"). It does not. The script never checks:
|
||||
- the dmg size or the three exported symbols (fact 1);
|
||||
- that no `svp` plugin is present (fact 2);
|
||||
- the `init.cxx` line numbers (fact 4);
|
||||
- bd2d0bec67 or `salvd.cxx` (fact 5);
|
||||
- `configure.ac` (fact 6).
|
||||
Scenario: a different dmg that drops `lok_preinit`, or ships an svp plugin, still passes `lok.sh`, and the doc claims those facts were asserted.
|
||||
Fix: assert the binary facts (`nm -gU`, the plugin list, the dmg size) in `lok.sh`, and give the source facts as plain citations.
|
||||
|
||||
docs/engineering-risks.md:22 — "与补丁第二处相同" and "需要补丁第三处" point to the wrong hunks. The patch's second hunk (`@@ -174`) is the `vcl/sysdata.hxx` include. The guard change is hunk 3 (`@@ -4274`), and the CoreGraphics path is hunk 4 (`@@ -4283`).
|
||||
Scenario: a reader checking the claim against the patch compares the wrong hunk.
|
||||
Fix: cite the hunks by their header lines, or by the code they change.
|
||||
@@ -0,0 +1,37 @@
|
||||
FAIL
|
||||
|
||||
Round-2 defects:
|
||||
|
||||
- lok.sh detach: fixed. Cleanup retries `hdiutil detach` for up to ten seconds and fails the script if the detach never succeeds. It skips `rm -rf` while the image is still mounted. Two runs in a row both exited 0 in about 10 s. `hdiutil info` showed no leftover LibreOffice image afterwards.
|
||||
- Skia-off build and build status: fixed. `engines/lo-src` is at 8221e31, with `ENABLE_SKIA=TRUE` and `SKIA_GPU=METAL` in `config_host.mk`. The build is running (about 38 clang processes). Fact 7 and the status paragraph now say this.
|
||||
- md5.c and CFLAGS: fixed. The CFLAGS override is gone. A fresh build has `CFLAGS = -g -O2 -W -Wall`. The md5.c sentence is now limited to 6dc099a.
|
||||
- libetpan fallbacks: partly fixed. The script passes `--with-json`, the test asserts `HAVE_OPENSSL` and `HAVE_JSON`, and json-c is listed. The OpenSSL fallback is still open; see the first entry.
|
||||
- mupdf.sh pkcs7 stub: fixed. The script passes `HAVE_LIBCRYPTO=yes`, and the test runs `nm -u` and looks for `PKCS7_`. A fresh build in a scratch tree passed: library sizes 53 940 840 and 9 905 040 bytes, and 17 PKCS7/EVP imports.
|
||||
- The doc's "逐条断言" claim: fixed. lok.sh now asserts the dmg size, the three exports and the plugin list. Facts 5 to 8 are given as citations. The cited lines match the source: `init.cxx:4277/4348`, `salvd.hxx:46-50`, `configure.ac:5950-5952`, `gdiimpl.cxx:1390-1393`, `salgdiutils.cxx:112` and `SkiaHelper.cxx:256-261`.
|
||||
- Hunk citations: fixed. The patch is described by the code it changes. It is byte-identical to the diff in `engines/lo-src`.
|
||||
|
||||
What I ran:
|
||||
|
||||
- `tests/engines/lok.sh` twice: exit 0 both times.
|
||||
- `tests/engines/mupdf.sh`: exit 0, both cached and from a fresh build.
|
||||
- `tests/engines/libetpan.sh`: exit 0, both cached and from a fresh build (86 s).
|
||||
- `tests/scripts/*.sh` and `agents_md.py`: exit 0.
|
||||
|
||||
scripts/build-libetpan.sh:16, :11, :29 — The OpenSSL fallback is still silent, and a TLS-less build gets cached as good.
|
||||
- `brew --prefix openssl@3` exits 0 and prints `/opt/homebrew/opt/openssl@3` even when that formula is not installed. I checked this with `brew --prefix libressl`, which returned a path that does not exist. So the `|| { ... exit 1; }` branch only runs when Homebrew itself is missing.
|
||||
- When `OPENSSL_DIR` or the brew prefix points at a directory with no OpenSSL, libetpan's `configure.ac:227-237` sets `with_openssl=no`. The build then succeeds, writes `PIN`, and prints "built libetpan".
|
||||
- Line 11 keys the cache on the pin alone, so every later run reuses that build.
|
||||
Scenario: I ran `OPENSSL_DIR=/nonexistent tests/engines/libetpan.sh` in a scratch tree. The build succeeded, and the test printed "FAIL: libetpan built without TLS". I reran the test with the working Homebrew OpenSSL. The build script printed "ok" from the cache, and the test failed with the same message again. It keeps failing until someone deletes `engines/libetpan` by hand. The doc's claim at `docs/engineering-risks.md:47` that the script fails outright ("缺了脚本直接失败") is false for this case.
|
||||
Fix: before building, require `$ssl/include/openssl/ssl.h`. After configure, fail if `config.h` lacks `HAVE_OPENSSL` or `HAVE_JSON`, and do so before `PIN` is written.
|
||||
|
||||
docs/engineering-risks.md:27-29 — PRD §23 requires recording the source commit, compile options and patches when a version is chosen ("选择版本时记录源码提交、编译选项和补丁"). R-01 picks a self-built LibreOffice, and the build is already running. Its configure options are recorded nowhere in the repo:
|
||||
- They exist only in `engines/lo-src/autogen.lastrun`, and `engines/` is not tracked.
|
||||
- There are 32 options, among them `--enable-mergelibs`, `--disable-python`, `--without-java`, `--with-lang=` and `--with-fonts`.
|
||||
- There is no `scripts/build-libreoffice.sh`, and the doc does not list the options.
|
||||
- How `engines/lo-src` was created (the clone, the tag, then `git apply` of the patch) is also unrecorded.
|
||||
Scenario: C07.1 runs on another machine, or `engines/` is cleared. Nobody can rebuild the binary that `ELY_LOK_EXPECT_TILE=written` is meant to verify. lok.sh's plugin-list assertion depends on `--enable-mergelibs` in a way nobody can trace.
|
||||
Fix: add a `scripts/build-libreoffice.sh` that clones the pin, applies the patch and runs autogen with the explicit option list. Alternatively, record the exact option list in R-01. Either way, `engines/lo-src` must be reproducible from the repo.
|
||||
|
||||
docs/engineering-risks.md:24 — The doc says the six compile errors come from "该块(`:794-810`)". In a release build the `assert` is compiled out. The block at 794-810 accounts for three of the errors: two `SkiaHelper` uses at :802 and `CAMetalLayer` at :807. The other three come from a second unguarded block in `windowWillExitFullScreen`, at :841 (two `SkiaHelper` uses) and :846 (`CAMetalLayer`).
|
||||
Scenario: a reader who patches or guards only lines 794-810 still gets three errors.
|
||||
Fix: cite both blocks (:794-810 and :833-849), or cite the error lines themselves.
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
scripts/build-libetpan.sh:11 — R3 的无 TLS 缓存问题仍可复现。缓存命中只检查 PIN 和 libetpan.a,直接跳过新增的 OpenSSL 与配置检查。实测在隔离目录用锁定源码构建 `--without-openssl --with-json` 的静态库,保留旧脚本会写入的 PIN 和 BUILD-config.h;随后使用正常 OpenSSL 连续执行两次当前构建脚本,均退出 0 并打印 `ok libetpan`,两次执行测试均退出 1,报 `libetpan built without TLS`。因此升级脚本后,R3 已产生的坏缓存仍会永久复用。修复方向:缓存命中前核对 BUILD-config.h 中 HAVE_OPENSSL、HAVE_JSON 均为 1;缺失或不符时使缓存失效并重新构建,加入旧坏缓存的恢复测试。
|
||||
|
||||
tests/engines/libetpan.sh:14 — 构建失败的具体诊断被重定向进临时 build.log,退出时第 8 行的 trap 随即删除该文件。实测在无缓存的隔离目录执行 `OPENSSL_DIR=/nonexistent tests/engines/libetpan.sh`,只收到 `FAIL: build failed, see .../build.log`,该路径在命令返回后已不存在;直接运行构建脚本才可看到 `no OpenSSL headers under /nonexistent`。新增前置检查失败以及 configure、编译失败时,指定的复核入口都会丢失错误原因,违反出错即暴露的规则。修复方向:失败时先将构建日志输出到 stderr,再清理临时目录;增加失败路径测试,断言原始诊断可见。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,28 @@
|
||||
FAIL
|
||||
|
||||
Checked: `cargo check --workspace`, `cargo clippy --workspace --all-targets` (clean), `cargo test --workspace` (3 passed), `cargo fmt --check` (clean), `tests/scripts/apps-start.sh` (8 of 8 pass), `tests/scripts/agents_md.py` (pass). Every file is under 500 lines and every comment is within limits. All eight app manifests match and inherit the AGPL license from the workspace.
|
||||
|
||||
1. crates/ely-platform/src/logging.rs:10 (and Cargo.toml:21,25, crates/ely-platform/Cargo.toml:12-13)
|
||||
What is wrong: logging is built on `log` + `env_logger`. agents.md:90 (rule 4) requires `tracing`.
|
||||
Failing scenario: a later task follows rule 4 and writes `tracing::info!` or a `tracing` span on a critical path, such as a LOK worker start. No `tracing` subscriber is installed, so the record is dropped without any error. It reaches neither stderr nor `ely-<app>.log`.
|
||||
Direction: install a `tracing-subscriber` filtered by `ELY_LOG` that writes to the same file and stderr, and bridge gpui's `log` records into it (`tracing-log`). The other option is to change rule 4 in agents.md to `log`. Code and rule must agree.
|
||||
|
||||
2. crates/ely-ui/src/shell.rs:25
|
||||
What is wrong: the outer `div()` exists only to carry `debug_selector("shell")`. That call does nothing unless gpui is built with `test`/`test-support` (gpui div.rs:887-894), and nothing in the repo queries "shell". The result is dead code plus a box inside a box around the `FocusScope` root.
|
||||
Failing scenario: a release or normal debug build gets an extra full-size layout node that does nothing. Searching `crates apps tests scripts` for "shell" turns up no reader of the selector.
|
||||
Direction: remove the wrapper and the selector, and return the `FocusScope` root directly.
|
||||
|
||||
3. crates/ely-ui/src/shell.rs:34
|
||||
What is wrong: the only content is an `h1` with the app name, which already appears in the window title (run.rs:28). agents.md rule 7 forbids useless titles and text.
|
||||
Failing scenario: opening ElyDoc shows "ElyDoc" in the title bar and again as a large heading in the middle of the window. The heading adds no information.
|
||||
Direction: drop the heading and leave the themed content area empty until a later task gives it real content.
|
||||
|
||||
4. Cargo.toml:28-29
|
||||
What is wrong: `unexpected_cfgs` declares `check-cfg` for `feature = "cargo-clippy"`, but no crate in the workspace uses `cfg(feature = "cargo-clippy")`. This is dead configuration.
|
||||
Failing scenario: removing the two lines leaves `cargo check` and `cargo clippy` output unchanged.
|
||||
Direction: delete the `[workspace.lints.rust]` entry.
|
||||
|
||||
5. tests/scripts/apps-start.sh:17
|
||||
What is wrong: the script sandboxes only `HOME`. `directories::BaseDirs::data_local_dir` uses `$XDG_DATA_HOME` first on Linux, which PRD §4.1 lists as a target.
|
||||
Failing scenario: on a Linux machine or CI runner with `XDG_DATA_HOME` set, the eight apps append to the developer's real `$XDG_DATA_HOME/ElyOffice/logs`. `find "$work"` at line 20 finds nothing, so the script reports "wrote no log file" even though logging works.
|
||||
Direction: point `XDG_DATA_HOME` at the temp dir (or unset it) for each app run, as is done for `HOME`.
|
||||
@@ -0,0 +1,13 @@
|
||||
PASS
|
||||
|
||||
Checks run: `cargo check --workspace` (clean), `cargo clippy --workspace --all-targets` (clean), `cargo fmt --check` (clean), `cargo test --workspace` (3 passed), `tests/scripts/apps-start.sh` (8 of 8 open a window, draw, and quit), `tests/scripts/agents_md.py` (117 names are public). Every file is under 500 lines, and every comment fits on one line.
|
||||
|
||||
The five round-1 defects:
|
||||
|
||||
1. Fixed. Logging still uses `log` + `env_logger`. agents.md rule 4 now says `log`, matching gpui. Neither the code nor the rules mention `tracing` anywhere.
|
||||
2. Fixed. crates/ely-ui/src/shell.rs:20 returns the `FocusScope` root directly. The wrapper and `debug_selector` are gone.
|
||||
3. Fixed. The repeated `h1` app name is removed. The window shows only the themed content area.
|
||||
4. Fixed. The `[workspace.lints.rust]` `check-cfg` entry is removed from Cargo.toml.
|
||||
5. Fixed. tests/scripts/apps-start.sh:17 sets `XDG_DATA_HOME` to a path inside the temp dir.
|
||||
|
||||
The changed code (Cargo.toml, agents.md, crates/ely-ui/src/run.rs, crates/ely-ui/src/shell.rs, tests/scripts/apps-start.sh) has no new defects. Showing a reason when the graphics backend fails to start (PRD §4.1) belongs to C01.2, so it is out of scope here.
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
Checks run: `cargo check --workspace`, `cargo clippy --workspace --all-targets`, `cargo fmt --check` (all clean); `cargo test --workspace` (5 passed); `tests/scripts/bundle-macos.sh`, `desktop-files.sh`, `installer-windows.sh`, `apps-start.sh` (all pass, none skipped). Release bundle `packaging/macos/bundle.sh ely-pdf` built, verified with `codesign --verify --deep --strict`, and printed `ready ElyPDF` under `ELY_SMOKE=1`. `otool -L` shows only system libraries, and the binary's minos is 11.0, below the plist's 14.0. Both appimagetool 1.9.1 SHA-256 values in appimage.sh match the downloaded release assets. Every file is under 500 lines, and every comment fits on one line in ten words or fewer.
|
||||
|
||||
1. packaging/windows/installer.nsi:41 — The installer ships only `${APP_SLUG}.exe`. The repo has no `.cargo/config.toml` setting `+crt-static`, so a `x86_64-pc-windows-msvc` build links `VCRUNTIME140.dll` dynamically. That DLL is not part of Windows 11. This breaks PRD §4.2: "独立安装包自带所需运行库". Failing scenario: install on a clean Windows 11 machine (for example Windows Sandbox) and start the app. Windows reports "VCRUNTIME140.dll was not found" and the app never opens. Fix: link the CRT statically for the windows-msvc target (`-C target-feature=+crt-static` in `.cargo/config.toml`), or have the installer carry the VC runtime DLLs next to the exe. Add a check that the shipped exe imports no `VCRUNTIME*.dll`.
|
||||
|
||||
2. packaging/linux/appimage.sh:59-65 — The AppDir holds only the binary, the desktop file and the icon. No shared library is bundled. gpui_linux links `xkbcommon` with the `x11` and `wayland` features, so the binary needs `libxkbcommon.so.0` and `libxkbcommon-x11.so.0` at load time. Neither is on the AppImage excludelist of libraries the host is expected to provide. This breaks PRD §4.2: "每个应用也有独立发行包,并附带其所需运行库". Failing scenario: on the Arch/Omarchy test snapshot (§4.1), where `libxkbcommon-x11` is a separate package that is not installed, `ElyDoc-x86_64.AppImage` exits with "error while loading shared libraries: libxkbcommon-x11.so.0". Fix: deploy the binary's non-excluded dependencies into `usr/lib` of the AppDir (linuxdeploy, or ldd filtered by the excludelist) with an `$ORIGIN` rpath. Fail the build if a needed library cannot be resolved.
|
||||
@@ -0,0 +1,16 @@
|
||||
FAIL
|
||||
|
||||
Checks run: `cargo check --workspace`, `cargo clippy --workspace --all-targets` and `cargo fmt --check` are all clean. `cargo test --workspace` passes 5 tests. `tests/scripts/bundle-macos.sh`, `desktop-files.sh`, `installer-windows.sh`, `appimage-pins.sh` and `apps-start.sh` all pass, and none is skipped. Every file is under 500 lines.
|
||||
|
||||
Round 1 defects:
|
||||
|
||||
1. installer.nsi, VCRUNTIME140.dll: fixed. `.cargo/config.toml` sets `+crt-static` for `cfg(all(windows, target_env = "msvc"))`. I ran `cargo check --target x86_64-pc-windows-msvc -p ely-platform -v`, and every target `rustc` call it prints carries `-C target-feature=+crt-static`. gpui_windows loads no other non-system DLL at link time. `nvapi`/`amd_ags` are optional `LoadLibrary` calls, and `D3DCompileFromFile` exists only under `debug_assertions`.
|
||||
2. appimage.sh, missing shared libraries: fixed. linuxdeploy 1-alpha-20251107-1 is pinned for both architectures, and both hashes match the downloaded assets (`appimage-pins.sh`). Its source (`src/core/elf_file.cpp:244-250`, `src/core/appdir.cpp:372-374`) throws `DependencyNotFoundError` on an ldd "not found" line and fails the deploy, so the comment at line 78 is accurate.
|
||||
|
||||
New defects:
|
||||
|
||||
1. packaging/linux/appimage.sh:70-76 — Lines 72, 74, 75 and 76, and the two `usr/share` directories created at line 70, do work that linuxdeploy redoes on line 79, so their output is never used. That is dead code (rule 6). Per the linuxdeploy source: `deployDesktopFile` copies the `--desktop-file` into `usr/share/applications/`; `deployIcon` copies the `--icon-file` into `usr/share/icons/hicolor/<its real size>/apps/`; `symlinkFile` removes the root `.desktop`/`.png` and replaces them with symlinks; and the `AppRun` symlink is created the same way when none exists. The hand-made copy at line 74 is also wrong whenever the icon is not 256×256. Failing scenario: `assets/icons/ely-doc.png` is delivered at 512×512. Line 74 puts it under `hicolor/256x256/apps`, and linuxdeploy adds a second copy under `hicolor/512x512/apps`. The AppImage then ships a 512 px image declared as 256 px, and desktop integration installs that copy into the user's 256×256 icon theme directory. Fix: keep only the desktop entry that linuxdeploy reads (line 73, or write it to `$work`). Delete the other manual copies and the `AppRun` link, and let linuxdeploy place them.
|
||||
|
||||
2. tests/scripts/installer-windows.sh:2 — The comment has eleven words ("The Windows installer builds, keeps user data, needs no runtime DLL."). Rule 5 caps comments at ten words. Failing scenario: `review.sh` and the rule-5 check reject the file as written. Fix: shorten it to ten words or fewer.
|
||||
|
||||
3. tests/scripts/installer-windows.sh:32 — The check that the C runtime is linked statically is a plain substring grep over the whole file. It cannot tell whether the flag applies to the msvc target. Failing scenario: someone comments the line out (`# rustflags = ["-C", "target-feature=+crt-static"]`) or moves it under `[target.x86_64-pc-windows-gnu]`. The test still prints "needs no runtime DLL", while an msvc build again imports `VCRUNTIME140.dll` and fails to start on a clean Windows 11 machine. Fix: parse `.cargo/config.toml` (for example with `python3 -c 'import tomllib…'`) and assert that `target."cfg(all(windows, target_env = \"msvc\"))".rustflags` contains `target-feature=+crt-static`. An alternative is to assert that the flag appears in `cargo check --target x86_64-pc-windows-msvc -v` output.
|
||||
@@ -0,0 +1,15 @@
|
||||
FAIL
|
||||
|
||||
Checks run: `cargo check --workspace`, `cargo clippy --workspace --all-targets` and `cargo fmt --check` are all clean, and `cargo test --workspace` passes 5 tests. `tests/scripts/bundle-macos.sh`, `desktop-files.sh`, `installer-windows.sh`, `appimage-pins.sh` and `apps-start.sh` all pass, and none is skipped. Every file is under 500 lines.
|
||||
|
||||
Round 2 defects:
|
||||
|
||||
1. appimage.sh, the hand-made AppDir copies: fixed. Only the desktop entry is written, into `$work`. linuxdeploy places the binary, the desktop file, the icon and `AppRun`.
|
||||
2. installer-windows.sh:2, the comment length: fixed. It is now ten words.
|
||||
3. installer-windows.sh, the substring grep for crt-static: fixed. The test now reads the effective cfg for the msvc target. I checked that it can fail: `RUSTFLAGS="" cargo rustc -p ely-platform --lib --target x86_64-pc-windows-msvc -- --print cfg` drops `target_feature="crt-static"`, so grep exits 1. Without the override, the line is present.
|
||||
|
||||
New defects:
|
||||
|
||||
1. packaging/linux/appimage.sh:76 — appimagetool is called without `--runtime-file`. In appimagetool 1.9.1 (`src/appimagetool.c`, `src/appimagetool_fetch_runtime.cpp`), a missing runtime file makes it download `https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-$arch` at build time. That URL is a moving tag, and the tool checks only HTTP 200 and the content length, with no hash. This runtime becomes the executable header of every AppImage we ship. It is the one binary the recipe pulls without a pin, and `tests/scripts/appimage-pins.sh:2` ("Every AppImage tool the recipe downloads matches its pinned hash") states the opposite, with no evidence. Failing scenario: upstream pushes a new `continuous` runtime, whether broken or compromised. The next `appimage.sh ely-doc` embeds it silently, so two builds of the same commit produce different AppImages, and nothing fails. Fix: pin a dated type2-runtime release per architecture and check it with `fetch_tool` against a sha256. Pass it with `--runtime-file`. Add the runtime URLs to `appimage-pins.sh`.
|
||||
|
||||
2. tests/scripts/installer-windows.sh:33-34 — `cfg=$(... cargo rustc ... 2>&1)` drops cargo's exit status. Any cargo failure then falls through to the grep and is reported as "the msvc build links the C runtime dynamically". Cargo's real error is swallowed and a wrong cause is shown (rule 2). Failing scenario: a dependency added later fails to compile for `x86_64-pc-windows-msvc`, or the registry cannot be reached. The test fails with the crt-static message, and the developer hunts in `.cargo/config.toml` for a problem that is not there. Fix: make a non-zero cargo exit fail the test with cargo's own output (for example `cfg=$(...) || fail "cargo rustc for msvc failed: $cfg"`), and grep only after it succeeds.
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,27 @@
|
||||
FAIL
|
||||
|
||||
Ran `cargo test -p ely-platform` (all pass) and `cargo clippy -p ely-platform --all-targets` (clean). Probes A–C ran against a local server in a scratch crate that depends on `ely-platform`.
|
||||
|
||||
1. crates/ely-platform/src/update.rs:221-230: The rule "https only, loopback excepted" can be bypassed with userinfo. The code checks the URL by string prefix, and `"http://127.0.0.1:" + anything` passes. ureq treats `127.0.0.1:1@` as userinfo and connects to the host after `@`.
|
||||
Scenario (probe B): `check(.., "http://127.0.0.1:1@192.168.31.221:PORT/latest.json")` returned `Ok(Available(..))`. The server logged `host: 192.168.31.221`, so the manifest came over plain http from a non-loopback host. Whoever controls the network then picks the URL and the sha256, and the downloaded file passes verification. `plain_http_is_for_this_machine_only` does not cover this case.
|
||||
Fix: parse the URL with `http::Uri` or `ureq::http::Uri`, reject userinfo, and decide by scheme plus the parsed host. Add the userinfo case to the test.
|
||||
|
||||
2. crates/ely-platform/src/update.rs:77, 232-246: `ensure_secure` checks only the first URL. Both agents use ureq's defaults (`max_redirects` 10, `https_only` false), so redirects to `http://` are followed for the manifest and for the download.
|
||||
Scenario (probe A): `http://127.0.0.1:P/redirect` answered 302 to `http://192.168.31.221:P/latest.json`. `check` followed it and returned `Available`. An `https://` manifest URL that redirects to `http://` is followed the same way, so the manifest arrives over plain http.
|
||||
Fix: check every hop. Either turn redirects off (`max_redirects(0)`) and follow `Location` manually through `ensure_secure`, or use `https_only(true)` for non-loopback URLs. Add a test where a redirect leaves loopback.
|
||||
|
||||
3. crates/ely-platform/src/update.rs:175-180, 239-246: The comment "the user can cancel" is not true. The download agent has no read timeout, and the cancel flag is checked only between `read` calls. A stalled connection blocks `read` forever: cancel does nothing, and no error is ever returned.
|
||||
Scenario (probe C): the server sent the headers and 1000 of 1000000 bytes, then went silent. I set `cancel` to true 1 s in. `download` was still blocked 19 s later. In the field, a dropped Wi-Fi link or a hung CDN leaves the download hung for good.
|
||||
Fix: put an idle-read timeout on the socket (for example a custom connector or transport with `set_read_timeout`) so that `read` returns, `cancel` is honoured, and a stall becomes an error. Add a test with a stalled server.
|
||||
|
||||
4. crates/ely-platform/src/update.rs:62, 66-73: `APPIMAGE` is an inherited environment variable, and it overrides the package-manager prefixes. A distro-installed binary launched from inside any AppImage is treated as `SelfManaged`.
|
||||
Scenario: the user starts a terminal emulator or editor that ships as an AppImage, then runs `/usr/bin/ely-doc` (installed by pacman) from its terminal. `APPIMAGE` is set, so `install_policy()` returns `SelfManaged` and the app would update itself over files the package manager owns. This breaks PRD §4.2 ("Linux 包管理器安装的版本不绕过包管理器自行覆盖文件").
|
||||
Fix: count it as an AppImage only when `current_exe` lies under `$APPDIR` (the mount of this AppImage), not just because `APPIMAGE` exists. Cover the inherited-variable case in the unit test.
|
||||
|
||||
5. crates/ely-platform/src/update.rs:142-147: The download's own error is lost when removing the `.part` file fails, because the `?` on `remove_file` returns the removal error instead.
|
||||
Scenario: the hash does not match, and on Windows an antivirus scanner still holds `ElyDoc.exe.part`. The caller sees "cannot remove …part", and "the file has sha256 X, the release says Y" never reaches the user.
|
||||
Fix: keep the original error and attach the removal failure to it, or log the removal failure and still return the original error.
|
||||
|
||||
6. crates/ely-platform/src/update.rs:76-102, 138-141: Rule 4 (key paths are logged) is not met. `check` makes a network request and logs neither the request nor the outcome (current, available, or which failure). A cancelled download is not logged either. Only a successful download logs anything.
|
||||
Scenario: QA-25 asks for an audit of outgoing update requests, and a user reports "check for updates does nothing". The log has no line showing a check was made or what it returned.
|
||||
Fix: log the manifest host and the result in `check`, and log a cancelled download (version only, no secrets).
|
||||
@@ -0,0 +1,17 @@
|
||||
FAIL
|
||||
|
||||
Ran `cargo test -p ely-platform` (11 + unit tests pass) and `cargo clippy -p ely-platform --all-targets` (clean). The probe ran in a scratch crate that depends on `ely-platform` and talks to a local server.
|
||||
|
||||
Round 1 defects:
|
||||
1. Userinfo bypass: fixed. `secure_host` parses with `Uri`, refuses `@` in the authority, and decides by parsed scheme and host. Covered by unit and integration tests.
|
||||
2. Redirects not checked: fixed. Both agents use `max_redirects(0)`, and `fetch` runs every hop through `secure_host`. Covered by `a_redirect_is_followed_on_this_machine_and_refused_when_it_leaves_for_plain_http`.
|
||||
3. Stall blocks cancel forever: the caller-facing part is fixed (cancel and the idle error both return). The fix leaves the blocked read behind, see new defect 1.
|
||||
4. Inherited `APPIMAGE`: fixed. The policy now depends only on the executable path.
|
||||
5. Download error lost on cleanup failure: fixed, with a test that makes the cleanup fail.
|
||||
6. Missing logs: fixed. `check` logs host and outcome, and `download` logs cancel and failure. Covered by a test.
|
||||
|
||||
New defects:
|
||||
|
||||
1. crates/ely-platform/src/update.rs:182-200, 208-228: A stalled or cancelled download leaks its reader thread and its socket. The socket still has no read timeout. `stream` stops waiting on the channel, but the spawned thread stays blocked in `reader.read` until the peer closes or sends data. A half-open connection (Wi-Fi dropped, CDN hung, no TCP keepalive) never does either, so the thread, the 64 KiB buffer and the connection stay alive for as long as the process runs.
|
||||
Scenario (probe): a local server sends the headers and 1000 of 1000000 bytes, then keeps the connection open and silent. Ten `download` calls with `idle` = 200 ms each returned "the download stalled" as expected. 3 s later the process had 13 threads instead of 3, and 20 more TCP descriptors than before (10 client, 10 server). Each "retry" after a stall adds one more. The current test can't see this because its server closes after 60 s and the test process exits first.
|
||||
Fix: put the idle timeout on the socket itself, for example a ureq `Transport`/connector that sets `set_read_timeout(idle)` on the `TcpStream`. Then `read` returns, the thread (or the plain loop that can replace it) ends, and the connection drops. Add a test that keeps the connection open, then asserts that the thread or connection is gone after the stall error, for example that the server sees EOF or reset.
|
||||
@@ -0,0 +1,8 @@
|
||||
PASS
|
||||
|
||||
Ran `cargo test -p ely-platform` (12 integration tests and the unit tests pass; the integration suite passed 5 runs in a row), `cargo clippy -p ely-platform --all-targets` (clean), `cargo fmt -p ely-platform --check` (clean), `cargo check --workspace` (ok), `tests/scripts/comments.py` and `tests/scripts/agents_md.py` (both pass).
|
||||
|
||||
Round 2 defect:
|
||||
1. Stalled or cancelled download leaks its reader thread and socket: fixed. `IdleConnector`/`Idle` caps every transport wait at `idle`, so ureq's `TcpTransport` sets a socket read timeout. The blocked `read` returns, the thread ends, and the connection drops. Probe (scratch crate, same silent server as round 2): ten stalls at `idle` = 200 ms each failed with "the download stalled" in about 204 ms. 2 s later the process had 2 threads and 1 TCP descriptor, the same as before the stalls. Five instant cancels returned `None` in about 1 ms each. Right after them there were 12 threads and 11 TCP descriptors, and 2 s later they were back to 2 and 1. `a_stalled_download_fails_and_frees_its_socket` and `a_cancel_is_answered_at_once_and_the_socket_is_freed_within_the_idle_time` check that the server sees EOF or reset.
|
||||
|
||||
No new defects found in the changed code (`update.rs` idle transport, the stall and cancel tests, and the `///`/`//!` handling in `comments.py`).
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
Ran: `cargo test -p ely-platform` (all pass, including `tests/startup.rs`), `cargo clippy -p ely-platform -p ely-ui --all-targets` (clean), `ELY_NO_DIALOG=1 tests/scripts/apps-start.sh` (8 of 8). Also built a scratch probe against the pinned gpui 1a28cff with `runtime_shaders`, the same features the apps use: `catch_unwind(|| gpui_platform::application().run(|_| panic!(..)))`.
|
||||
|
||||
1. crates/ely-ui/src/run.rs:22-35 — on macOS the `catch_unwind` never catches a startup panic. gpui runs the `run` callback from `did_finish_launching`, an `extern "C" fn` (gpui_macos/src/platform.rs:1317). A panic there cannot unwind, so the process aborts. Scenario: the probe printed `panic in a function that cannot unwind … thread caused non-unwinding panic. aborting.` and exited 134, without printing `caught`. So `open`, `ely_gpui_component::init`, `Theme::set_mode_now`, `Shell::new` and Metal renderer creation (`expect("error building metal library")`, gpui_apple/src/metal_renderer.rs:219-224) all end in an abort with no dialog and no "could not start" text. Windows behaves the same way inside `extern "system" window_procedure`. Fix direction: install the failure path in a panic hook (`logging::init_in` already sets one) so it runs before the abort, or have it call `startup::fail` from the hook during startup. Do not rely on unwinding out of `run`.
|
||||
|
||||
2. crates/ely-ui/src/run.rs:57-68 — the "window did not open" branch cannot catch the case §4.1 is about on macOS. `MacPlatform::open_window` returns `Err` only for `AnchoredPopup` (gpui_macos/src/platform.rs:693). The renderer is built without returning a `Result` (gpui_macos/src/window.rs:1101). When no Metal device is found, gpui logs and calls `std::process::exit(1)` (gpui_apple/src/metal_renderer.rs:205-208). Scenario: on a Mac or VM without a usable Metal device, ElyDoc exits 1 with only gpui's log line. No dialog appears and `startup::fail` never runs, which breaks "无法初始化图形后端时显示原因". Fix direction: check for a Metal device yourself before `run` (macOS), or do the equivalent wgpu adapter probe (Linux/Windows), and call `startup::fail` with the reason. Alternatively, patch gpui so renderer creation returns `Err` through `open_window`, and record it in `patches/`.
|
||||
|
||||
3. crates/ely-ui/src/run.rs:22-35 — on Linux, where the event loop is Rust and panics do unwind, `catch_unwind` covers the app's whole lifetime, not just startup. Scenario: a panic an hour into editing unwinds out of `run`, and the user sees "ElyDoc could not start … the graphics backend did not start: <panic>". That is a false reason in the log and the dialog (rule 2: no masking or mislabeling). Fix direction: limit this path to startup. For example, set a "started" flag after the first window opens and stop treating later panics as startup failures. The real panic text should go through the normal panic path.
|
||||
|
||||
4. tasks/TASKS.md:21 — "本机验证:…窗口打不开与启动期 panic 都走这条路" has no evidence. No test or script makes a window fail or makes a startup panic go through `run`. `tests/startup.rs` calls `startup::fail` directly, and `apps-start.sh` covers only the success path. Findings 1 and 2 show the claim is false on macOS, the only machine it was "verified" on. Fix direction: add a test or smoke hook that triggers a startup panic and a window-open failure through `ely_ui::run` (for example an env-gated fault in a test binary) and asserts exit 1, the stderr text and the log line. Until that passes, move these two paths under 未验证.
|
||||
@@ -0,0 +1,14 @@
|
||||
FAIL
|
||||
|
||||
Ran: `cargo test -p ely-platform -p ely-ui` (all pass, including `tests/startup.rs` and the harness-less `tests/startup_panic.rs`: "2 cases pass"), `cargo clippy -p ely-platform -p ely-ui --all-targets` (clean). I also ran the `startup_panic` "launch" child without `ELY_NO_DIALOG`. A system alert came up that named ElyDoc, the reason and a log path, and it blocked until the process was killed. Read gpui 1a28cff `gpui_apple/src/metal_renderer.rs:190-210`: `graphics::check` uses the same `Device::all()` / `system_default()` test that gpui uses before its `process::exit(1)`.
|
||||
|
||||
Round 1:
|
||||
|
||||
1. Startup panic aborting on macOS: fixed. `startup::guard` (startup.rs:36-48) calls `fail` from the panic hook. That runs before the non-unwinding abort. `startup_panic.rs` "launch" proves it inside gpui's launch callback: exit 1, stderr text and log line.
|
||||
2. Metal device missing, so gpui exits silently: fixed. `graphics::check` runs before `run` (run.rs:17-19), and its probe matches gpui's own. The failure branch is honestly listed under 未验证.
|
||||
3. Late panics labelled as a failed start: fixed. The `STARTED` flag is set on the first frame (run.rs:48). `startup_panic.rs` "late" shows a later panic exits 101 with the ordinary panic text and no "could not start".
|
||||
4. Unbacked claims in TASKS: fixed. The window-open branch and the probe-failure branch moved to 未验证. The panic path has a test.
|
||||
|
||||
New:
|
||||
|
||||
1. crates/ely-platform/src/startup.rs:19-22 — `fail` names a log file it has not checked exists. It rebuilds the path from `data_dir(app)` and does not ask where logging actually writes, or whether logging started at all. Scenario A: `~/Library/Application Support/ElyOffice/ely-doc` is not writable. `logging::init` fails and run.rs:15 calls `fail`. The dialog and stderr then say "日志 / Log: …/ely-doc/ely.log", but that file was never created, and the `log::error!` on line 18 goes nowhere because no logger is set. The user is sent to a log that does not exist (rule 2: no default standing in for missing data). Scenario B, seen in this review: the `startup_panic` child logs to its temp dir, yet the dialog points at the real user data dir. Neither test checks the path, so this went unnoticed. Fix direction: have logging record the path it actually opened (for example `init` returns it or stores it), and have `fail` print that path. When logging did not start, say there is no log. Add an assertion on the printed log path to `tests/startup.rs`.
|
||||
@@ -0,0 +1,9 @@
|
||||
PASS
|
||||
|
||||
Ran: `cargo test -p ely-platform -p ely-ui`: all pass, including `tests/startup.rs` (2), `tests/logging.rs` and the harness-less `startup_panic` ("2 cases pass"). `cargo clippy -p ely-platform -p ely-ui --all-targets`: clean. `ELY_SMOKE=1 ELY_NO_DIALOG=1 ely-doc`: "ready ElyDoc", exit 0. `ely-doc` with a read-only `HOME`: exit 1, stderr gives the real reason ("cannot create …/ElyOffice/ely-doc: Permission denied") and "日志 / Log: none, logging did not start".
|
||||
|
||||
Round 2:
|
||||
|
||||
1. `fail` named a log file it had not checked: fixed. `logging::init_in` stores the path it actually opened in `LOG_PATH`, and only after `try_init` succeeds (logging.rs:53). `fail` prints `logging::path()`, or says there is no log (startup.rs:19-22). `tests/startup.rs` asserts that stderr names the temp-dir log. It also asserts that without a logger the text says "none" and names no `ely.log`. I confirmed the read-only-home case by hand on the real binary.
|
||||
|
||||
New: none found. Each file is under 500 lines. Each comment is one line of ten words or fewer. Every public item has a caller. The 未验证 list in TASKS.md still matches what was not exercised.
|
||||
@@ -0,0 +1,28 @@
|
||||
FAIL
|
||||
|
||||
Checked: `cargo test -p ely-ui -p ely-platform` passes (ely-ui 6 unit tests, ely-platform 10 + 12 + startup_panic). `tests/scripts/theme.sh` passes on macOS. `cargo clippy --all-targets` on both crates is clean. The three fixtures match `themes/{tokyo-night,catppuccin-latte,white}/colors.toml` in basecamp/omarchy at f45461a, so the "real palettes" claim holds. Contrast numbers below come from a probe crate outside the repo that `#[path]`-includes `omarchy.rs` and calls `parse` on the fixtures.
|
||||
|
||||
1. crates/ely-ui/src/theme/omarchy.rs:73 (and the test at :244)
|
||||
What is wrong: `readable` and `toward_bg` lift text only against `bg`. The test also checks only against `p.bg`. The components put this text on `surface`, `sunken`, `hover`, `active` and `selection`, which are mixed toward `fg`, so the lifted colors drop below 4.5 there. Ely's own palettes stay at or above 4.5 on every one of those surfaces except `selection`. The TASKS line ("全部正文色达 4.5:1") and agents.md ("每个正文色拉到 4.5:1") claim more than the code does.
|
||||
Failing scenario: catppuccin-latte gives `fg_muted` on `surface` 4.35, `fg_subtle` on `surface` 4.30 and on `active` 3.70. tokyo-night gives `fg_subtle` on `surface` 4.20 and on `active` 3.44. white gives `fg_subtle` on `sunken` 3.98. `shell::OfflineIndicator` (status.rs:128,133) draws `fg_muted` on `surface`, so under catppuccin-latte its label reads 4.35:1.
|
||||
Fix direction: lift each text token against the darkest or lightest surface it is drawn on (bg, surface, sunken, hover, active), not just bg. Extend the test to assert every text token against each of those surfaces.
|
||||
|
||||
2. crates/ely-ui/src/theme/omarchy.rs:75-84
|
||||
What is wrong: only the named keys (`red`, `green`, `blue`, …, `selection`) are read. Omarchy v4's own `colors.toml` format also carries `color0`..`color15`, `purple`/`bright_purple` and `selection_background`. Omarchy's resolver (`bin/omarchy-theme-color`) aliases red→color1, green→color2, yellow→color3, blue→color4, magenta→color5/purple, cyan→color6, and selection→selection_background. `omarchy-theme-colors-from-alacritty` writes only `accent`, `selection`, `background`, `foreground` and `color0`..`color15`. Every theme without its own `colors.toml` (for example a user or git-installed theme that ships `alacritty.toml`) therefore reaches Ely in that form. Each missing hue then silently becomes Ely's default through `unwrap_or(p.danger)` and the like, with no log line. That is a default value hiding missing data (rule 2), and the theme is not synced.
|
||||
Failing scenario: parse a generated file (`accent`, `selection`, `background`, `foreground`, `color0`..`color7` from Tokyo Night). The probe shows `palette.danger == Palette::dark(false).danger` and `success` equal to Ely's default. Syntax `keyword` comes from Ely's chart, not the theme's `color5`. Danger, success, warning, info, chart, ANSI and syntax colors all show Ely's palette under an Omarchy theme.
|
||||
Fix direction: resolve keys with Omarchy's alias cascade (named key, then `colorN`, then `purple`/`selection_background`). Where a key is still missing, log it rather than substituting silently, and add a fixture in the generated `colorN` form.
|
||||
|
||||
3. crates/ely-ui/src/theme/mod.rs:37
|
||||
What is wrong: `ELY_OMARCHY_SYNC` treats every value except `"0"` as on, without a warning. `ELY_APPEARANCE` (:32) and `ELY_REDUCE_MOTION` (accessibility.rs:20) both warn on an invalid value. This is a silent fallback on a documented 0/1 switch.
|
||||
Failing scenario: `ELY_OMARCHY_SYNC=false` or `off` leaves Omarchy sync on, and the log does not say the value was ignored.
|
||||
Fix direction: match `"0"`, `"1"` and unset explicitly, and warn on anything else, as the other two switches do.
|
||||
|
||||
4. crates/ely-ui/src/theme/watch.rs:40
|
||||
What is wrong: when the live theme directory has no `colors.toml`, `reload` logs at debug and keeps the previous `OmarchyState`. The app keeps showing the theme the user just left.
|
||||
Failing scenario: the app runs under tokyo-night, and the user runs `omarchy-theme-set` with a user theme that has no `colors.toml` and no `alacritty.toml`, so Omarchy writes none. `theme/` is swapped and `theme.name` changes, but Ely stays on the tokyo-night palette. Nothing above debug level records this.
|
||||
Fix direction: on `Ok(None)` after a change, clear `OmarchyState`, re-apply (Ely's palette or the chosen appearance), and log at info.
|
||||
|
||||
5. crates/ely-platform/src/dirs.rs:19-21
|
||||
What is wrong: the default Omarchy directory comes from `BaseDirs::state_dir()`, which honours `$XDG_STATE_HOME`. Omarchy hardcodes `$HOME/.local/state/omarchy/current` (`bin/omarchy-theme-set`: `CURRENT_THEME_PATH="$HOME/.local/state/omarchy/current/theme"`).
|
||||
Failing scenario: on an Omarchy machine with `XDG_STATE_HOME=$HOME/.state`, Ely looks in `~/.state/omarchy/current`, logs "no Omarchy on this machine", and never syncs.
|
||||
Fix direction: build the path from the home directory as `.local/state/omarchy/current` to match Omarchy, with `ELY_OMARCHY_DIR` still taking precedence.
|
||||
@@ -0,0 +1,32 @@
|
||||
FAIL
|
||||
|
||||
Checked: `cargo test -p ely-ui -p ely-platform` passes (ely-ui 10 omarchy tests plus startup_panic, ely-platform 12 + 10 + 2 + 1). `cargo clippy -p ely-ui -p ely-platform --all-targets` is clean. `tests/scripts/theme.sh` passes on macOS, including the live swap, the empty theme and the return. `tests/scripts/references.sh`, `agents_md.py` and `comments.py` pass. All 22 fixtures are byte-identical to `reference/omarchy/themes/*/colors.toml` at f45461a. `accessibility.rs` and `env.rs`, built alone in a scratch crate, `cargo check` cleanly for x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. That backs the cross-compile claim; `cargo check -p ely-platform --target …` itself cannot run here because ring has no C cross compiler. Contrast figures below are WCAG ratios worked out from the fixture hex values, where `danger`, `warning`, `success` and `info` equal the raw `red`, `yellow`, `green` and `blue`.
|
||||
|
||||
Round 1:
|
||||
1. Fixed. `palette.rs:28-48` lifts against bg, surface, overlay, sunken, hover and active. `tests.rs:65` checks every listed token on all six for all 22 themes.
|
||||
2. Fixed. `colors.rs:11-27,111-133` follows Omarchy's alias cascade, the `colorN` fixture checks it, and hues that are still missing are logged in `watch.rs:38`.
|
||||
3. Fixed. `env::flag` warns on any value other than 0 or 1, and `theme.sh:64` covers `ELY_OMARCHY_SYNC=false`.
|
||||
4. Fixed. `watch.rs:47-51` clears the state, re-applies and logs at info. `theme.sh:99-100` checks this live.
|
||||
5. Fixed. `dirs.rs:21-27` builds the path from home and ignores `XDG_STATE_HOME`.
|
||||
|
||||
New defects:
|
||||
|
||||
1. crates/ely-ui/src/theme/omarchy/palette.rs:102 (and tests.rs:42)
|
||||
What is wrong: `success`, `warning`, `danger` and `info` are the theme's raw hues and are never lifted. The component library draws text in them: `overlays::PromptDialog`'s validation reason (dialogs.rs:299), the required-field star (forms/structure.rs:156), git badges, the merge marker and others. Ely's own palettes keep all four at or above 4.5 on bg (light 4.82–5.33, dark 6.37–9.97). The test's "every text token" list leaves them out. So the TASKS line ("全部正文色在六种表面上达 4.5:1") and agents.md ("每个正文色…都拉到 4.5:1") claim more than the code does.
|
||||
Failing scenario: 11 of the 22 bundled themes put a status hue under 4.5:1 on bg alone. Some examples: nord red 3.05, solitude red 2.78, miasma red 2.30, catppuccin-latte yellow 2.31 and green 2.96, rose-pine yellow 2.05, matte-black yellow 2.90. Under nord, a PromptDialog error reads at 3.05:1 on the plain background and lower on the dialog's surface.
|
||||
Fix direction: lift each status color for text the way the syntax colors are lifted, or keep the hue for fills and add a lifted text variant if the component library allows it. Add the four tokens to `text_on_surfaces`. If that is not done, narrow the claim in TASKS.md and agents.md.
|
||||
|
||||
2. crates/ely-ui/src/run.rs:33-37
|
||||
What is wrong: `ELY_SMOKE` treats every value except `hold` as one-frame smoke and treats an unset or non-UTF-8 value as off. Neither case gives a warning. That is the same silent fallback round 1 flagged for `ELY_OMARCHY_SYNC`. The new `hold` mode is also missing from agents.md:111, which documents only `ELY_SMOKE=1`.
|
||||
Failing scenario: `ELY_SMOKE=0 cargo run -p ely-doc` opens a window, draws one frame and quits, and the log does not say why.
|
||||
Fix direction: match `"1"`, `"hold"` and unset explicitly, warn on anything else (through `env::value`), and document `hold` in agents.md.
|
||||
|
||||
3. crates/ely-platform/src/accessibility.rs:11-13
|
||||
What is wrong: when the OS gives no answer, it logs "using full motion" before it looks at `ELY_REDUCE_MOTION`. The log then contradicts the decision that follows.
|
||||
Failing scenario: on Linux without gsettings, with `ELY_REDUCE_MOTION=1`, the log reads "the OS gives no reduce-motion answer, using full motion" and then "theme: reduced motion on".
|
||||
Fix direction: log the fallback only when both the environment variable and the OS give no answer, or log the source the decision came from.
|
||||
|
||||
4. crates/ely-platform/src/accessibility.rs:4-6
|
||||
What is wrong: `pub fn system_reduce_motion` only forwards to `os_reduce_motion`. Its sole caller is `reduce_motion` in the same file, so it is a public layer with one user (rule 8).
|
||||
Failing scenario: no behavior breaks. It is an extra public entry point with no caller outside the file.
|
||||
Fix direction: call `os_reduce_motion` from `reduce_motion` and delete the wrapper.
|
||||
@@ -0,0 +1,11 @@
|
||||
PASS
|
||||
|
||||
Checked: `cargo test -p ely-ui -p ely-platform` passes (ely-ui 11 unit tests including `run::tests` and startup_panic, ely-platform 12 update tests and the rest). `cargo clippy -p ely-ui -p ely-platform --all-targets` is clean. `tests/scripts/theme.sh` passes on macOS, including the live swap, the empty theme and the return. `references.sh`, `agents_md.py` and `comments.py` pass. All 22 fixtures are byte-identical to `reference/omarchy/themes/*/colors.toml` at f45461a. I built `env.rs` and `accessibility.rs` alone in a scratch crate with the project toolchain (1.96.0), and `cargo check` passes for x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc, so the cross-compile claim still holds after the wrapper was removed. Every file is under 500 lines.
|
||||
|
||||
Round 2:
|
||||
1. Fixed. `palette.rs:130-133` lifts `success`, `warning`, `danger` and `info` against bg and their `*_subtle`. `TEXT_PAIRS` (`palette.rs:32-35`) and `verify` check them. These pairs match the library's `tooling/a11y.rs:35-38` exactly. `tests.rs:73-80` asserts all four on all 22 themes, and `tests.rs:123-130` checks that a red which already reads is kept. The claims in TASKS.md ("正文与状态色 4.5:1") and agents.md ("各自的表面") now match the code.
|
||||
2. Fixed. `run.rs:36-46` matches unset, `1` and `hold` explicitly and warns on anything else through `env::value`. `run.rs:93-99` tests this, and agents.md:112 documents `hold`.
|
||||
3. Fixed. `accessibility.rs:6-8` logs the full-motion fallback only when neither the variable nor the OS gives an answer.
|
||||
4. Fixed. The wrapper is gone, and `reduce_motion` calls `os_reduce_motion` directly.
|
||||
|
||||
New defects: none found in the changed code.
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
1. `crates/ely-ui/src/shell/mod.rs:95`:页面键作为整个内容区动画祖先的 ID,切页会重建持续显示控件的元素状态,违反 agents.md 的动画与焦点规则。已用公开 Shell API 在临时副本构建并运行探针:内容区始终显示同一个 `Button::new("next-page", ...)`,按钮回调增加页码并通知重绘,`Shell::page(("page", page))` 跟随页码。按 Tab 聚焦,再连续按两次 Return,实际只到第 1 页;第一次转场后焦点环消失,日志出现 `focus: its element left the tree; the root takes it`。保持动画键的对照组可连续触发同一按钮。修复方向:保持内容树及控件的祖先 ID 稳定,用独立转场状态记录页面键变化和起始时间,并补充持续显示控件的键盘焦点回归测试。
|
||||
|
||||
2. `tests/scripts/shell.sh:14`:测试仅检查启动器输出 `ready ElyDoc`,没有验证 C05.2 的布局或切页行为;现有 Slide 单测也只验证插值计算。已实跑原实现的工作区构建、测试、八应用启动及 shell 冒烟;随后仅在临时副本中将 `Shell::render` 传给左右 SidePanel 的视图都过滤为 None,彻底移除有内容的侧栏,重跑该脚本仍以退出码 0 输出成功。这使任务的核心布局回归无法被测试发现。修复方向:加入实际布局和交互断言,覆盖左右侧栏有/无内容、one → two → one 的展开与收起,以及转场后的焦点;让上述删除侧栏的变异实现触发失败。
|
||||
|
||||
3. `crates/ely-ui/src/shell/mod.rs:20`:新增注释违反单行、最多十词的明确规则。这里的文档注释跨两行,第 21 行有 16 词;`crates/ely-ui/src/run.rs:13` 同样跨两行,第 14 行有 12 词。另外 `crates/ely-ui/examples/shell.rs:1`、`crates/ely-ui/src/shell/blank.rs:6`、`crates/ely-ui/src/shell/panel.rs:15`、`:55`、`:61` 分别为 12、14、13、14、15 词。运行 `python3 tests/scripts/comments.py` 已返回退出码 1 并列出这些范围内的位置。修复方向:将必要注释压缩到一行且最多十词,删除冗余说明,并重新运行注释检查。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
tests/scripts/shell.sh:24:上一轮第 2 项的布局验证仍有缺口。新增探针用 `is_focused` 判断区域显隐,缺少侧栏和内容区的实际宽度断言。已在临时副本验证:删除左右侧栏的变异会使脚本失败;将 `crates/ely-ui/src/shell/panel.rs:100` 的空视图分支改为 `return div().flex_none().w(cx.theme().sidebar_width(false)).h_full().into_any_element();`,使 page two 收起属性内容后仍保留完整空栏,重跑脚本却以退出码 0 输出成功。这使 PRD §5.2“无内容的侧栏不占位”的核心回归继续漏检。修复方向:断言左右侧栏有/无内容时的实际布局边界,以及 one → two → one 转场结束后的侧栏宽度和内容区宽度,确保空栏占位变异触发失败。
|
||||
|
||||
crates/ely-ui/src/shell/mod.rs:24:上一轮第 3 项的多行注释仍保留。第 24–25 行组成两行文档注释,`crates/ely-ui/src/run.rs:13–14` 同样如此,违反 agents.md 的单行注释规则。当前 `python3 tests/scripts/comments.py` 返回退出码 0,因为它只统计每行词数,漏掉连续文档注释。修复方向:将这两处必要注释各压缩为一行且最多十词,并补充多行文档注释的检查。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/commands.rs:51 — macOS 将唯一的 File 菜单作为首个应用菜单处理,实际界面缺少 File 菜单,违反 UX-01 的文件菜单入口要求。复现:构建并启动 ElyDoc.app,原生 accessibility 菜单栏仅有 ElyDoc;展开 ElyDoc 后才出现 New、Open…、Save、Save As…、Export…、Print…。八个应用共用此菜单定义。修复方向:macOS 在 File 前提供应用菜单,使 File 成为独立的文件菜单,并核对实际原生菜单栏。
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:86 — ToggleCommandPalette 直接翻转布尔值,关闭路径跳过组件的焦点归还,丢失打开面板前的控件焦点。复现:在 shell gallery 聚焦右栏属性控件,连续两次发送 Command+Shift+P;面板关闭后 props.is_focused(window) 为 false,日志显示焦点落到根作用域。相同起点使用 Esc 或选择命令关闭时均为 true。该路径违反组件库要求的每个浮层关闭路径归还焦点,也使后续键盘操作离开原控件。修复方向:让快捷键关闭与面板自身关闭使用一致的焦点恢复流程,并覆盖重复切换后的焦点断言。
|
||||
|
||||
tests/scripts/shell.sh:35 — 命令验收绕过快捷键、菜单点击和命令面板选择,无法检出 C05.3 的关键接线失败。crates/ely-ui/examples/shell.rs:186 直接调用 commands::run 并 dispatch_action 打开面板,191–193 行直接执行命令并关闭面板。实测临时副本中分别删除 run.rs 的 commands::bind_keys(cx),以及将 Root 的 CommandPalette.on_run 改为空回调,两次 shell.sh 都以 0 退出并报告 commands、palette pass;原版 workspace check、workspace test、shell.sh 和八应用启动测试也均通过。修复方向:通过实际按键、菜单选择及面板输入和选择触发命令,断言执行结果与关闭状态,使这两种断线变异均触发测试失败。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
tests/scripts/shell.sh:34 — 上一轮第 3 项的菜单验收仍有缺口:探针只读取菜单标签,命令执行来自快捷键和面板,菜单点击及其动作映射均未验证。复现:在临时副本中将 crates/ely-ui/src/commands.rs:65 的 `MenuItem::action(SAVE.label, Save)` 改成 `MenuItem::action(SAVE.label, Open)`,运行 shell.sh 仍以 0 退出并报告 menus、commands、palette pass;此时菜单 Save 绑定 Open,Cmd+S 绑定 Save,UX-02 要求的相同结果已被破坏。修复方向:实际选择文件菜单项并断言执行的命令,逐项与对应快捷键结果比较,使该错接变异触发失败。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
tests/scripts/shell.sh:37 — 本轮新增的菜单动作批量执行掩盖了快捷键错误:crates/ely-ui/examples/shell.rs:223–239 在发送 Cmd+S 后执行全部文件菜单动作,脚本仅检查整份日志是否出现 Save,最终 recent 也被后续动作覆盖。复现:在临时副本中将 crates/ely-ui/src/commands.rs:77 的 `KeyBinding::new(SAVE.keys, Save, Some(CONTEXT))` 改成 `KeyBinding::new(SAVE.keys, Open, Some(CONTEXT))`,运行 `bash tests/scripts/shell.sh` 仍以 0 退出并报告全部通过;此时 Cmd+S 执行 Open,文件菜单 Save 执行 Save,违反 UX-02 的相同结果要求。上一轮菜单 Save→Open 变异已被新增映射断言检出,但快捷键验收出现回归。修复方向:逐项独立触发菜单选择和对应快捷键,在每次触发后立即断言实际执行的命令,再比较两条路径的结果,使快捷键错接或缺失触发测试失败。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/examples/shell.rs:283 — 新增的菜单结果断言读取刚由同一命令的快捷键写入的 `recent()[0]`,菜单零次执行也会被判为成功。上一轮 Cmd+S→Open 变异已被本轮测试检出,但菜单验收仍受前一次执行结果污染。复现:在临时副本中将本文件 `Probe::Actions` 分支内的 `window.dispatch_action(action, cx)` 替换为 `drop(action)`,停用全部菜单动作派发后运行 `bash tests/scripts/shell.sh`,仍以 0 退出并报告 menus、commands、palette pass。修复方向:每次触发前记录执行日志位置,触发后断言新增且仅新增一次对应命令,再比较菜单与快捷键的结果,确保漏执行触发失败。
|
||||
|
||||
crates/ely-ui/examples/shell.rs:501 — 本轮扩充后的文件共 508 行,违反 agents.md 第 5 条的 500 行上限。复现:`wc -l crates/ely-ui/examples/shell.rs` 输出 508,第 501 至 508 行已越界。修复方向:整理重复的探针流程,将该文件控制在 500 行以内。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:316 — 删除对话框把任意未修饰的 Enter 松键都送到 `delete_confirmed`,忽略当前焦点。复现:打开 Page One 的删除确认,按 Tab 让 Cancel 显示焦点环,再按 Enter;实际删除 Page One,只剩 Page Two,违反 UX-03、UX-07。隔离探针与截图均确认此路径。修复方向:让 Cancel 和 Delete Page 各自响应键盘激活,通过组件的确认及关闭流程执行操作并归还焦点;补 Cancel 上 Enter/Space 保留对象的测试。
|
||||
|
||||
crates/ely-ui/src/tasks.rs:65 — `tick(done)` 在前台实体更新闭包内同步执行,长任务会阻塞窗口和取消操作,违反 UX-05。复现:以 600 毫秒的同步工作模拟一次导出步骤;探针确认回调与 UI 位于同一线程,原定 50 毫秒执行的 UI 定时器延迟至约 628 毫秒。现有任务测试的回调立即返回,覆盖不到此路径。修复方向:将实际工作放入后台执行器或工作进程,向 UI 传回进度和结果,并使工作接受取消信号;测试耗时工作期间的窗口响应及取消。
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:171 — 删除最后一页后仍索引 `self.pages[0]`,并把选择设为已失效的 `[0]`。复现:沿现有探针先删除 Page Two,再确认删除剩余 Page One;进程因 `index out of bounds: the len is 0 but the index is 0` 中止。修复方向:为删除后的空页面集合清空当前页与选择,渲染空状态,让属性计算及重命名、删除入口处理空选择;补连续删除到零页的测试。
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:258 — 任务状态的唯一操作始终调用 `cancel_task`;`Failed` 状态下该调用直接结束,界面缺少 UX-06 要求的重试或另存路径。复现:运行已有磁盘满失败任务,界面停在 `Failed(The disk is full)`;调用状态项绑定的操作后仍保持同一失败状态。修复方向:保留失败任务的输入,在错误状态提供可执行的重试或另存入口,并验证恢复后使用原输入重新执行成功。
|
||||
|
||||
tests/scripts/shell.sh:57 — 混合态与长任务断言仅检查探针从模型生成的日志,无法验证 UX-04、UX-05 对应界面。实测两次独立变异:移除 `PropertyGrid` 的 `.group(group)` 后,脚本仍通过;移除任务状态栏的整个 `.when_some(task_label, ...)`,连同取消入口一起删除后,脚本仍以 0 退出并报告任务通过。修复方向:断言实际渲染的混合属性、任务范围、状态和错误,通过界面操作触发取消与恢复;让上述两种变异分别触发失败。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/tasks.rs:51 — `cancel()` 立即报告 `Cancelled`,后台工作仍可启动或继续,工作返回的错误也会在第 86 行被丢弃,违反 UX-05 和错误必须暴露的规则。隔离探针中,创建任务后同步取消,工作回调仍执行一次;另一任务在约 62 毫秒取消时写入计数为 0,约 795 毫秒时计数变为 1,状态仍为 `Cancelled`,回调返回的模拟写入错误消失。修复方向:派发工作前检查取消,把取消信号传给正在执行的工作,并在实际停止后报告取消完成;保留停止前产生的错误,补取消前后副作用与错误的断言。
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:274 — Cancel / Retry 的唯一入口是只绑定 `on_click` 的 `StatusBarItem`;当前锁定组件的该项没有 Tab 停靠点、焦点环或键盘激活处理,违反 UX-03。复现:运行磁盘满失败任务,从导航开始逐帧按 Tab 十二次再按 Enter,任务仍为 `Failed(The disk is full)`;原脚本通过鼠标点击同一项可以重试成功。修复方向:使用支持焦点与 Enter / Space 激活的组件提供任务操作,通用状态栏交互修复回组件库,并补仅用键盘取消和重试的测试。
|
||||
|
||||
tests/scripts/shell.sh:87 — 亮像素数量无法确认混合属性、任务范围和错误详情已渲染,上一轮的测试缺陷仍存在。两次独立变异均实测以 0 退出:移除 `PropertyGrid` 的 `.group(group)` 后运行 `ELY_APPEARANCE=light bash tests/scripts/shell.sh`;把任务文案改为省略进度范围的 `Export · Cancel` 和省略错误详情的 `Export failed · Retry` 后,在深色主题运行同一脚本。修复方向:固定截图主题,核对实际渲染的 Mixed、进度范围及错误文本,并让上述变异分别触发失败。
|
||||
|
||||
tests/scripts/ink.py:2 — 模块说明共有 14 个词,超过项目规定的十词上限。复现:按空白拆分该行说明得到 14,现有 `comments.py` 检查仍通过。修复方向:将这条说明缩短到十词以内,并让注释检查覆盖 Python 文档字符串。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
tests/scripts/shell.sh:98 — 混合属性的截图断言仍会放过违反 UX-04 的渲染,第二轮的测试缺陷尚存。复现:在临时副本中,仅将 `examples/shell/main.rs` 的属性行渲染改为 `group.row(name.clone(), if name == "Format" { "Text".to_string() } else { value.clone() })`,保留混合选择及模型日志;运行 `bash tests/scripts/shell.sh`,实测退出码为 0。截图明确显示两页被选中且 `Format` 为首个对象的 `Text`,区域哈希仍与 `Mixed` 基准完全一致,距离为 0。当前 16×16 平均亮度哈希无法验证属性值,调低第 104 行的容差也无法捕获此回归。修复方向:为实际渲染的属性值采用能区分 `Mixed` 与 `Text` 的断言,并将此变异加入验证,要求脚本失败。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 命令面板及窗口内容缺少 UX-12 要求的辅助技术名称、角色、状态和文本。实测打开中文画廊的命令面板,macOS AX 树只有窗口、系统窗口按钮和菜单,搜索框、命令结果、Rename/Delete 按钮与属性全部缺席;屏幕阅读器无法读取或操作这些内容。在临时副本为三个焦点锚点添加 role 和 aria_label 后,同一 AX 客户端立即读到 Inbox、Content、Properties,确认采集链正常。修复方向:在组件库补齐实际使用组件的可访问语义并更新依赖,为项目自绘控件补齐语义;增加原生辅助技术树及读写、选择、禁用状态的验收。
|
||||
|
||||
crates/ely-ui/examples/shell/dialogs.rs:27 — 此处接入的 PromptDialog 将输入法候选确认的 Enter key-up 当作重命名提交,违反 UX-09。按 macOS IME 回调顺序在临时探针中设置 marked text 为 ye,再提交“页”,随后仅派发 Enter key-up,实际得到 dialog_after_candidate_enter=false、标题变为“页”;用户确认候选时,对话框已经提交关闭。现有 probe.rs:283 直接注入最终汉字,shell.sh 的 CJK/撤销断言通过时仍遗漏此错误。修复方向:在组件库区分 IME 已消费的确认键和明确的表单提交,再更新依赖;回归覆盖组合、候选确认、继续输入、撤销及跨区域焦点。
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 中文目录只覆盖命令名称和分组,命令面板自身文案仍为英文,违反 UX-08。设置 ELY_LOCALE=zh-CN 后打开面板,实测仍显示 Type a command、Recent,查询无结果时显示 No matching commands;画廊 main.rs:269、dialogs.rs:14 的状态、操作和错误正文同样硬编码英文。shell.sh:107 的中文流程停在菜单检查,因而全部通过。修复方向:让这些可见文案与错误正文统一读取双语目录,组件固定文案在组件库接入国际化;中文验收继续覆盖面板、空结果、状态、对话框和错误。
|
||||
|
||||
crates/ely-platform/src/locale.rs:13 — macOS 使用区域格式 localeIdentifier 选择界面语言,遗漏系统首选语言。以 -AppleLanguages '(zh-Hans)' -AppleLocale en_US 启动且清除 ELY_LOCALE,原生探针读到 preferred=["zh-Hans"]、system=en-US;实际 ely-doc 冒烟日志也是 locale en-US -> en-US。选择中文界面并保留美国区域格式的用户因此得到英文界面,违反 UX-08。修复方向:按系统首选界面语言匹配受支持语言,将区域格式与界面语言分别处理,并增加二者不同的回归案例。
|
||||
|
||||
crates/ely-platform/src/locale.rs:28 — Linux 将高优先级的 C/POSIX 语言设置跳过,继续采用低优先级中文设置。将该 Linux 函数原样编译成临时探针,LC_ALL=C.UTF-8、LC_MESSAGES=zh_CN.UTF-8、LANG=zh_CN.UTF-8 返回 zh-CN;LC_MESSAGES=C、LANG=zh_CN.UTF-8 也返回 zh-CN。有效的英文覆盖因此失效。修复方向:采用首个非空的高优先级设置,遇到 C/POSIX 时解析为英文并结束查找;补上这两种优先级组合的测试。
|
||||
|
||||
tests/scripts/shell.sh:80 — 缺少 UX-10 指定的多屏缩放验收。当前 resized layout 断言仅检查逻辑窗口宽度;对应 probe.rs:159 改变窗口尺寸。本轮在临时副本记录 scale_factor,1100×720 → 800×600 → 1100×720 全程为 2,断言仍通过;测试也没有点击命中、文本选区或浮层位置的检查,无法检测跨屏 DPI 改变后的坐标错误。修复方向:增加 100%、150%、200% 以及跨屏变化的验收,分别核对点击目标、选区和浮层位置,明确记录三端实际运行结果。
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 上轮 UX-12 缺陷仍在。新增 role/aria 仅覆盖自绘锚点和任务按钮。本轮打开中文命令面板并检查原生 macOS AX 树,内容节点只有“收件箱”“Content”“属性”三个按钮;画面中的搜索框、命令结果、标签页和属性文本均缺席。关闭面板并点击“重命名…”后,AX 树仍无对话框、输入框和确认/取消按钮,屏幕阅读器无法获取这些控件的名称、角色、状态和文本。修复方向:在组件库补齐实际使用组件的辅助技术语义并更新依赖,增加原生 AX 树及输入、选择、禁用状态的回归验收。
|
||||
|
||||
crates/ely-ui/examples/shell/dialogs.rs:27 — 上轮 UX-09 缺陷仍在:PromptDialog 将输入法候选确认的 Enter key-up 当作重命名提交。本轮临时探针按组合输入回调设置 marked text 为 ye、提交“页”,随后只派发 Enter key-up,实测 dialog_after_candidate_enter=false、titles=页|Page Two。用户确认候选时,名称即被提交且对话框关闭。现有 shell.sh 全部通过,其 CJK 流程仍直接注入最终汉字。修复方向:在组件库区分输入法已消费的确认键与表单提交并更新依赖;回归覆盖组合、候选确认后继续输入、撤销和跨区域焦点。
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 上轮 UX-08 缺陷仍在:新增双语目录未覆盖组件内部文案及全部画廊文案。本轮 ELY_LOCALE=zh-CN 的实际画面仍显示 Type a command、Recent、No matching commands;重命名对话框仍显示 Cancel,main.rs:408 的 Content、main.rs:133 的 Mixed 也继续硬编码英文。中文用户在主要操作流程中得到混合语言界面,shell.sh:107 的中文验收仍在菜单阶段停止。修复方向:将剩余固定文案接入双语目录,组件文案在组件库修复后更新依赖,并检查中文面板、空结果、混合态和对话框。
|
||||
|
||||
tests/scripts/shell.sh:80 — 上轮 UX-10 的缩放验收缺口仍在。新增 resized click hits 日志未被脚本断言,probe.rs:170 唯一的 resize 仍设回启动尺寸 1100×720。本轮记录步骤 1、11、13,三次 viewport 均为 1100×720、scale_factor 均为 2,shell.sh 仍全部通过;100%、150%、200% 及跨屏变化下的点击、文本选区和浮层位置均无验收,相关坐标回归可通过现有测试。修复方向:实际改变缩放并跨屏运行,断言点击目标、选区和浮层位置,记录三端实际验证结果。
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:501 — 本轮文件已增长至 538 行,违反 agents.md 的单文件最多 500 行规则;按当前文件直接统计即可复现。修复方向:按现有职责拆分画廊区域渲染或状态处理,使每个文件保持在 500 行以内。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 上轮 UX-12 缺陷仍在。本轮运行中文画廊并读取原生 macOS AX 树,命令面板打开时内容节点仅有“收件箱”“内容”“属性”三个按钮,画面中的搜索框、命令结果、标签页和属性文本均缺席;关闭面板、点击“重命名…”后,AX 树仍缺少对话框、输入框和确认/取消按钮。屏幕阅读器无法获取主要交互控件的名称、角色、状态和文本。修复方向:在组件库补齐实际使用组件的辅助技术语义并更新依赖,增加原生 AX 树及输入、选择、禁用状态的回归验收。
|
||||
|
||||
crates/ely-ui/examples/shell/dialogs.rs:27 — 上轮 UX-09 缺陷仍在:PromptDialog 将输入法候选确认的 Enter key-up 当作重命名提交。本轮临时副本按输入法回调设置 marked text 为 ye、提交“页”,随后仅派发 Enter key-up,实测 dialog_after_candidate_enter=false、titles=页|Page Two。用户确认候选时,名称即被提交且对话框关闭。现有 shell.sh 全部通过,其 CJK 流程仍直接注入最终汉字。修复方向:在组件库区分输入法已消费的确认键与表单提交并更新依赖;回归覆盖组合、候选确认后继续输入、撤销和跨区域焦点。
|
||||
|
||||
crates/ely-ui/src/shell/root.rs:133 — 上轮 UX-08 缺陷仍在:组件内部固定文案尚未接入双语目录。本轮 ELY_LOCALE=zh-CN 的实际命令面板仍显示 Type a command、Recent,输入无匹配查询后显示 No matching commands;重命名对话框仍显示 Cancel。中文用户在主要操作流程中得到混合语言界面,shell.sh:108 的中文验收仍停在菜单阶段。修复方向:在组件库将面板、空结果及对话框固定文案接入国际化并更新依赖,补齐中文流程回归。
|
||||
|
||||
tests/scripts/shell.sh:82 — 上轮 UX-10 的缩放验收缺口仍在。新增断言仅要求 scale factor 含数字;probe.rs:170 唯一的 resize 仍设回启动尺寸 1100×720。本轮临时探针记录步骤 1、11、13,三次 viewport 均为 1100×720、scale_factor 均为 2,resized layout 与 resized click hits 均为 true,原始 shell.sh 全部通过。100%、150%、200% 及跨屏变化下的点击、文本选区和浮层位置仍无验收,相关坐标回归可通过现有测试。修复方向:实际改变缩放并跨屏运行,逐项断言点击目标、选区和浮层位置,记录三端实际验证结果。
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/examples/shell/dialogs.rs:50 — UX-12 的对话框语义仍缺失。本轮在 macOS 打开中文删除对话框,原生 AX 树只新增 Cancel 和“删除页面”两个按钮,画面中的“删除 Page One?”及永久删除说明均缺席;重命名输入 fail 后,“磁盘已满,名称未保存”也只出现在画面。屏幕阅读器无法获知删除对象、影响及提交失败原因。上游 Dialog 容器与标题、正文、校验错误仍缺少可访问语义。修复方向:在组件库补齐对话框角色、名称、说明和错误文本,更新依赖,并在原生 AX 树上验收这些状态。
|
||||
|
||||
reference/Ely-GPUI-Components/src/buttons/button.rs:293 — 新增 Button 角色后,disabled 状态仍只改变透明度和鼠标样式。本轮重命名输入 fail,画面的“重命名”按钮置灰,AX 树仍将其暴露为普通按钮,缺少禁用状态。辅助技术会把当前无法执行的提交读作可用操作。修复方向:将实际 disabled 状态同步到辅助技术属性,并覆盖可用到禁用及恢复的原生 AX 回归。
|
||||
|
||||
crates/ely-ui/examples/shell/main.rs:157 — UX-08 仍有主要流程固定英文。ELY_LOCALE=zh-CN 时,重命名输入框的占位符及 AX 名称仍为 Page name;重命名和删除对话框仍显示上游 PromptDialog/ConfirmDialog 写死的 Cancel。现有中文脚本在菜单阶段退出,这些混合语言界面仍使测试通过。修复方向:将输入框占位符和组件取消文案接入双语目录,并检查中文对话框及其辅助技术名称。
|
||||
|
||||
tests/scripts/shell.sh:82 — 上轮 UX-10 验收缺口仍在。断言仅要求 scale factor 含数字;probe.rs:170 的唯一 resize 设回启动尺寸 1100×720。本轮临时探针记录步骤 1、11、13,viewport 均为 1100×720、scale_factor 均为 2,resized layout 和 resized click hits 均通过。100%、150%、200% 及跨屏变化的点击、文本选区和浮层位置均未得到验证,缩放回归可通过现有测试。修复方向:实际覆盖指定缩放及跨屏变化,逐项核对点击目标、选区和浮层位置,并记录三端验收结果。
|
||||
|
||||
tests/scripts/shell.sh:83 — UX-09 的现有回归仍直接注入最终汉字,覆盖不到本轮修复的候选确认事件。本轮将临时上游副本的 Dialog Enter 处理恢复为旧 key-up 实现,完整 shell.sh 仍退出 0;同一副本经 marked text=ye、提交“页”、仅派发 Enter key-up 后,实测 dialog=false、titles=页|Page Two。当前 key-down 实现通过该重放,现有测试却无法区分两者。修复方向:把组合输入、候选确认的事件顺序纳入探针,断言对话框持续打开、标题保持原值,并覆盖继续输入、撤销和焦点切换。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
reference/Ely-GPUI-Components/src/overlays/dialogs.rs:162 — r4 的 UX-12 对话框正文缺陷仍在。ConfirmDialog 把 message 交给无辅助技术语义的 said() 子节点;本轮新增的 Dialog aria_description 只读取 detail,此调用没有设置 detail。本轮 macOS 中文删除对话框的画面显示“Page One及其内容将被永久删除。”,原生 AX 树中的 AXDialog 只有“删除Page One?”标题及“取消”“删除页面”按钮,正文、AXDescription 和 AXHelp 均缺失。屏幕阅读器仍无法获取永久删除的影响。修复方向:在组件库为确认正文提供可访问文本或对话框说明,更新依赖,并在原生 AX 树验证完整删除影响。
|
||||
|
||||
crates/ely-ui/examples/shell/probe.rs:321 — r4 的 UX-09 回归测试缺陷仍在。新增 key-up-only Enter 在撤销已清空输入框后执行,空名称校验会阻止提交,掩盖旧 key-up 处理器的提前提交。本轮仅在临时组件副本恢复旧 Dialog on_key_up 实现,完整 tests/scripts/shell.sh 仍退出 0;随后重放 marked text=ye、提交“页”、仅派发 Enter key-up,旧实现得到 dialog_after_candidate_enter=false、titles=页|Page Two,当前实现得到 true、titles=Appendix|Page Two。修复方向:把有效名称的组合输入和候选确认顺序纳入正式探针,在 key-up 后断言对话框保持打开、页面标题保持原值,并要求旧实现触发测试失败。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/settings/panels.rs:64 — 最新宽度仅由延迟 500 ms 的 detached task 保存,正常退出会丢弃尚在等待的改动,违反 C05.6 的重启恢复要求。真实 ElyDoc 窗口中从默认宽度向右拖动 80 点,松手后立即 Cmd+Q,再启动:侧栏回到默认 220 点,settings.json 只有 window/version,缺少 panels;独立重复三次均复现。方向:正常退出前提交并等待最新宽度写入,补上拖动后立即正常退出、重启检查宽度的回归测试;当前 panels.sh:51 先等待写入再终止,覆盖不到此路径。
|
||||
|
||||
crates/ely-ui/src/settings/panels.rs:34 — 已存在的无效宽度通过 as_f64()/and_then 转为 None,随后被 fit 当作缺省值,构成静默回退。复现:应用设置写入 {"version":1,"panels":{"left":"broken","right":320}} 后启动 ElyDoc,左栏恢复默认 220 点,日志没有宽度解析错误;真实窗口探针已确认。方向:区分缺省/null 与无效配置,对 panels 结构及已填写宽度的类型、有效数值做校验,传播具体错误并显示。
|
||||
|
||||
crates/ely-ui/src/settings/panels.rs:69 — 宽度写入失败只写日志,未接入界面错误状态或刷新错误提示,违反 PRD UX-06。复现:正常启动后把该应用的 settings.json 文件替换为同名目录,再拖宽左栏;窗口显示约 296 点的新宽度,日志记录 “panel widths not saved: Is a directory (os error 21)”,应用与设置窗口均没有保存失败提示,改动只留在内存。读取失败在本文件:28 也只有日志与默认宽度。方向:将应用偏好的读取、保存错误接入界面可见的错误状态,保留本次输入并提供重试,补上失败路径的真窗口断言。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/settings/mod.rs:133 — 通用偏好保存成功会清空 Current.unsaved,吞掉仍未解决的侧栏保存错误,违反 PRD UX-06。真实 ElyDoc 窗口中,把应用 settings.json 替换为同名目录后拖宽左栏,设置窗口显示保存失败;点击 Dark 成功保存通用外观后,该提示消失,应用 settings.json 仍为目录,侧栏宽度仍待保存。AX 与磁盘状态已验证,证据在 /tmp/c056-r2-evidence.log。方向:按保存操作记录错误,通用偏好的成功仅清除对应错误,侧栏错误保留到其保存成功;补上交错保存的真窗口断言。
|
||||
|
||||
crates/ely-ui/src/settings/panels.rs:102 — 侧栏成功重试只更新 saved,保留旧的保存失败提示,导致界面报告与实际保存结果冲突。真实窗口中先以同名目录触发宽度保存失败,再移除目录并再次拖动;settings.json 已成功写入 panels.left=349,重新打开设置窗口仍显示 “The last change was not saved: Is a directory (os error 21)”。AX 与保存字节已验证,证据在 /tmp/c056-r2-evidence.log;现有 panels.sh 在首次失败提示出现后即退出。方向:最新宽度保存成功时清除该操作的错误并刷新界面,保留其他操作的错误;补上修复保存路径、再次拖动、检查保存字节与提示恢复的回归测试。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-ui/src/settings/panels.rs:102 — 宽度保存成功只清除 unsaved[PANELS],启动时记录的 Current.app_error 持续保留,设置窗口继续报告已经修复的读取错误。真实 ElyDoc 窗口中,以 {"version":1,"panels":{"left":"broken","right":320}} 启动,再拖宽左栏;磁盘已保存有效的 panels.left=296、right=null,重新打开设置并等待控件进入 AX 树后,仍显示 “This app's settings cannot be read, so defaults are in use: settings: panels left cannot be "broken"”。重复复现,证据在 /tmp/c056-r3-evidence.log。方向:保存或重试成功后重新校验应用配置,清除已经恢复的对应读取错误,保留其他读取错误;补上无效配置经拖动修复后的真实窗口断言。
|
||||
|
||||
tests/scripts/panels.sh:114 — 重试恢复断言把重新打开设置时暂时为空的 AX 内容当作错误提示已清除,覆盖不到第二轮的提示残留缺陷。在使用独立 CARGO_TARGET_DIR 的隔离副本中,仅移除 panels.rs:104 的成功清错调用,完整 panels.sh 仍以退出码 0 输出 “ok: panels”。独立真实窗口探针确认:该断言执行时设置页的 Dark 控件尚未进入 AX 树;移入鼠标、等待控件加载后,仍可读到 “The last change was not saved: Is a directory (os error 21)”,磁盘宽度已保存。原始实现经相同探针确认提示成功清除,证据在 /tmp/c056-r3-evidence.log。方向:先确认设置页实际内容进入 AX 树,再检查提示状态,并要求移除成功清错调用的变异使测试失败。
|
||||
@@ -0,0 +1,15 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:192 — `reload()` 把修订号重置为 0,保存票据仅携带修订号,旧任务因此能够匹配重新加载后的新编辑,违反 FILE-03。隔离测试复现:编辑到 revision=1,取得保存票据并写盘;保存完成回调到达前调用 `reload()`,再编辑一次;随后 `finish_save(old_ticket, path)` 返回成功,状态实际为 `Saved`,revision 和 saved_revision 都是 1,此时重新加载后的编辑尚未写盘。修复方向:保证重新加载前后的修订身份唯一,使重新加载失效旧保存票据,并补充这个回调顺序的回归测试。
|
||||
|
||||
crates/ely-files/src/session.rs:194 — `reload()` 无条件设为 `Saved`,使只读视图获得保存权限,违反 §6.1 的只读副本约束。隔离测试中,`Session::read_only(path)` 经外部更新后调用 `reload()`,随后 `state()` 返回 `Saved`,`begin_save()` 返回成功;与原可写会话同时存在的视图因此可以写入同一文件。修复方向:重新加载保留访问模式,所有状态转换持续维护只读约束,并覆盖只读视图重新加载后保存被拒绝的测试。
|
||||
|
||||
crates/ely-files/src/session.rs:165 — 保存完成无条件覆盖较新的外部修改状态,同时把完成时读取到的磁盘指纹记成已保存指纹,违反 FILE-02。隔离测试复现:取得票据并写入本次保存内容,另一程序随后改写文件,调用 `external_modified()`,最后收到本次保存的完成回调并调用 `finish_save()`;实际状态变成 `Saved`,记录的指纹属于外部内容,会话内容仍属于本次保存修订。修复方向:保存完成只确认对应任务写入的版本,保留其后发生的外部修改通知及冲突状态,并增加乱序完成回调测试。
|
||||
|
||||
crates/ely-files/src/session.rs:398 — 去重仅比较规范化路径,硬链接会产生两个可写会话,违反 §6.1 的同文件聚焦要求。隔离测试通过 `std::fs::hard_link(doc, alias)` 创建同一文件的两个名字,依次执行 `open(doc, false)` 和 `open(alias, false)`,实际返回 `Fresh(0)`、`Fresh(1)`,第二次应聚焦已有会话。修复方向:结合文件系统文件身份识别硬链接别名,保留路径用于显示,并加入真实硬链接去重测试。
|
||||
|
||||
crates/ely-files/src/session.rs:392 — `canonicalize()` 的所有错误被 `unwrap_or_else` 吞掉并静默改用原路径,违反错误暴露规则,也会改变已打开文件的去重键。隔离测试先通过符号链接打开文件,删除其目标后再次以同一符号链接调用 `open()`,实际返回 `Fresh(1)`;底层 `canonicalize()` 已明确失败。对原本缺失的路径,`open()` 同样直接登记为新会话。修复方向:向调用方传播路径解析错误,失败时保持会话登记不变;为 `open()` 和 `is_open()` 补充解析失败测试。
|
||||
|
||||
crates/ely-files/src/session.rs:92 — FILE-02 要求的 `Loading` 状态不可达。`load()` 只读取元数据便构造 `Saved`,全部构造及转换方法均未设置 `Loading`,`state` 又是私有字段;当文档内核仍在异步加载大文件时,调用方无法用此会话表示加载中。源码检索确认 `SaveState::Loading` 唯一用处是显示字符串分支。修复方向:提供实际的加载开始、成功及失败转换,并测试加载完成前的状态及允许操作。
|
||||
|
||||
crates/ely-files/src/session.rs:202 — `SaveError` 是闲置的公开错误类型,违反无 dead code 规则。全项目检索仅找到定义和再导出;实际只读保存失败由 `begin_save()` 返回 `Err(SaveState::ReadOnly)`,既有只读测试也只验证该返回值,`SaveError::ReadOnly` 从未构造或消费。修复方向:删除闲置类型及导出,或让现有保存错误路径实际使用该类型,并同步测试。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:328 — 上轮路径解析错误问题仍有遗漏:`NotFound` 被转换为路径键并登记成功。隔离测试中,打开缺失文件得到 `Ok(Fresh(0))`;打开真实文件后删除它,再检查和打开,分别得到 `Ok(false)`、`Ok(Fresh(1))`,原登记仍存在。悬空符号链接测试已通过,但普通缺失路径的错误仍被吞掉;现有 `a_missing_path_registers_as_a_new_file` 还把这一行为写成预期。修复方向:向 `open()`、`is_open()` 的调用方传播解析失败,失败时保持登记不变;新建文件走无隐含目标路径的流程,并补充缺失、删除后的回归测试。
|
||||
|
||||
crates/ely-files/src/session.rs:320 — 上轮硬链接去重只修复了 Unix 分支,Windows 仍以规范化路径作为身份。同一文件的两个硬链接具有不同路径,依次打开会登记两个可写会话,违反 §6.1。抽取当前非 Unix `identity()` 原样在本机运行硬链接测试,两个键确实不同;Windows 目标的交叉检查通过,但报告 `Key::File` 从未构造,现有硬链接测试也被 `#[cfg(unix)]` 排除。修复方向:Windows 分支使用卷与文件身份去重,并让硬链接回归测试覆盖 Windows。
|
||||
|
||||
crates/ely-files/src/session.rs:315 — Unix 去重键只保存首次打开时的设备号和 inode,原子替换后同一路径失去已有会话关联,违反 §6.1。隔离测试先打开 `doc.md`,再通过临时文件加 `rename()` 替换它;随后 `is_open(doc.md)` 返回 `false`,再次打开得到 `Fresh(1)`,预期为 `Focus(0)`。正常原子保存或外部编辑器的原子替换都会触发两个可写会话。修复方向:保留规范化路径与文件身份的关联,在保存或替换后刷新身份,同时保留硬链接去重,并加入真实原子替换测试。
|
||||
|
||||
crates/ely-files/src/session.rs:208 — `begin_save_overwrite()` 直接生成票据,绕过只读、加载中及保存中的限制。隔离测试对 `Session::read_only(path)` 调用此方法,取得票据并完成保存后,状态变成 `Saved`,后续普通 `begin_save()` 也返回成功;§6.1 的只读视图因此获得持续写权限。上轮只读重载复现已通过,当前测试只检查普通保存入口。修复方向:所有保存入口共同维护访问模式与任务状态,覆盖确认仅解除外部冲突限制,并覆盖只读、加载中和已有保存任务的拒绝路径。
|
||||
|
||||
crates/ely-files/src/session.rs:183 — 未解决的外部修改会被普通状态转换清除,破坏 FILE-02 的状态表示与冲突保存守卫。隔离测试执行外部写入、`external_modified()`、`edit()` 后,状态变成 `Dirty`,普通 `begin_save()` 返回成功;保存期间收到外部修改后再调用 `fail_save()`,第 254 行同样把状态改成 `SaveFailed`,重试保存获准。两条路径都绕过了明确处理外部冲突的步骤。修复方向:独立保留未解决的外部修改事实,使编辑和保存失败持续维护冲突约束,覆盖这两种事件顺序。
|
||||
|
||||
crates/ely-files/src/session.rs:165 — 新增的 `finish_load()` 在完成回调时重新读取磁盘指纹,并无条件设为 `Saved`,会把加载之后的外部版本当作内存内容的基线。隔离测试让后台读取 14 字节内容,外部程序随后改写成 39 字节并调用 `external_modified()`,最后调用 `finish_load()`;实际记录的指纹长度为 39,状态为 `Saved`,内存仍是先前的 14 字节,违反 §6.1 的最后加载指纹要求及 FILE-02。修复方向:使用本次实际加载内容对应的指纹确认完成,保留其后发生的外部修改状态,并补充延迟完成回调的测试。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:277 — 外部变更将 Loading/Saving 直接改成 ExternalModified,导致第 213 行新增的覆盖保存守卫失效。隔离测试中,加载期间收到 external_modified() 后,begin_save_overwrite() 返回成功;已有修订 1 保存任务时收到外部变更、编辑到修订 2,也能启动第二个保存任务。进一步按“修订 2 写盘、修订 1 写盘、修订 1 完成回调、修订 2 完成回调”重放,实际得到 state=Saved、revision=2、saved_revision=2,磁盘内容仍为修订 1,违反 FILE-03。修复方向:独立维护加载与进行中的保存任务,外部冲突事件持续保留任务限制,覆盖确认仅解除冲突限制;补充加载中变更和保存交错完成的回归测试。
|
||||
|
||||
crates/ely-files/src/session.rs:389 — 文件身份只在已命中登记后的第 392 行刷新,原子替换后仍可能使用过期 inode 去重。隔离测试打开 doc.md 为 Fresh(0),通过临时文件 rename 完成原子保存并调用 finish_save(),再给新文件建立 alias.md 硬链接;is_open(doc.md) 为 true,is_open(alias.md) 为 false,打开 alias.md 得到 Fresh(1),产生两个可写会话,违反 §6.1。另一测试保留旧文件的硬链接后替换 doc.md,打开旧链接反而得到 Focus(0),聚焦了内容与目标均已不同的会话。修复方向:替换后同步更新登记身份,并在硬链接匹配前核实缓存身份与登记路径的当前文件一致;覆盖替换后的新旧硬链接场景。
|
||||
|
||||
crates/ely-files/src/session.rs:187 — 上轮外部冲突修复保住了普通保存守卫,但编辑仍将 ExternalModified 改成 Dirty,隐藏尚未解决的外部修改。隔离测试执行外部写入、external_modified()、edit() 后,begin_save() 返回 ExternalModified,state() 却返回 Dirty;冲突标志为私有字段,显示层无法通过会话状态持续显示外部已修改,违反 FILE-02。现有 tests.rs:159 还将 Dirty 写成预期。修复方向:编辑后继续向显示层暴露未解决的外部修改状态,并让回归测试断言冲突提示持续存在。
|
||||
|
||||
crates/ely-files/src/session/tests.rs:265 — 上轮要求的 Windows 硬链接回归仍被 #[cfg(unix)] 排除,新增 Windows 文件身份分支缺少硬链接去重的行为测试。Windows 编译会直接删除 hard_links_share_one_session;若该平台重新退化为按路径去重,同一文件两个硬链接生成两个可写会话的缺陷仍缺少检测。本轮 18 项原有单测及 Windows --tests 编译检查均通过,硬链接行为仅在本机 Unix 执行。修复方向:让现有硬链接回归覆盖 Windows,并在 Windows 上运行该测试验证卷与文件身份去重。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:249 — 重载后的旧保存回调直接返回 StaleTicket,新增的 saving 标志持续为 true。隔离测试执行 begin_save()、写盘、外部修改、reload()、旧任务 finish_save()、edit();旧回调返回 StaleTicket,随后 state() 为 Dirty,begin_save() 与 begin_save_overwrite() 均持续返回 Saving,已结束的任务永久阻塞后续保存。现有 a_reload_kills_old_tickets 只断言修订与显示状态,遗漏恢复保存。修复方向:独立跟踪进行中的任务身份,在旧任务结束时释放对应槽位,同时继续拒绝其过期内容确认;补充重载、旧任务结束、再次保存的回归测试。
|
||||
|
||||
crates/ely-files/src/session.rs:226 — 加载失败限制仍依赖可被外部事件覆盖的显示状态。隔离测试执行 Session::loading(path)、fail_load()、外部写入、external_modified()、begin_save_overwrite(),实际得到 fingerprint=None 与 Ok(SaveTicket { generation: 0, revision: 0 })。文件内容尚未成功加载,覆盖保存已获准,可将空白或残缺的内存内容写回文件;上一轮任务守卫修复仍遗漏失败加载后的变更事件。修复方向:独立保留加载成功与编辑可用性,所有保存入口持续检查该条件,覆盖确认只解除外部冲突;补充加载失败、外部变更、覆盖保存的拒绝测试。
|
||||
|
||||
crates/ely-files/src/session.rs:399 — 实时身份匹配将 file_id(have) 的所有错误转换为 false,静默失去同文件去重。macOS 隔离测试先打开 restricted/doc.md 为 Fresh(0),给它建立可访问的 alias.md 硬链接,再撤销 restricted 目录的访问权限;已登记路径的 metadata 返回 PermissionDenied,is_open(alias.md) 却返回 Ok(false),open(alias.md, false) 返回 Ok(Fresh(1))。同一 inode 形成两个可写会话,违反 §6.1,并吞掉实际文件系统错误。修复方向:匹配流程返回可传播的错误,权限等身份查询失败通过 open()/is_open() 暴露且保持登记不变;补充已登记路径失去访问权限后的硬链接回归测试。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:262 — 旧票据在身份校验前无条件清空 saving,能够释放另一个正在运行的保存任务。上一轮的单次旧回调释放场景已通过;隔离测试继续执行“旧任务开始、reload、旧任务完成被拒绝、新任务开始、旧完成回调重复到达、新任务写盘并完成”,实际新任务的 finish_save() 返回 StaleTicket,state() 持续为 Saving;重复旧回调之后也已解除并发保存守卫。现有回归仅检查空闲时重复完成,遗漏新任务进行中的重复回调。修复方向:记录可核对的进行中任务身份,仅由对应任务释放槽位;过期内容确认继续拒绝,并覆盖旧回调与新任务重叠的测试。
|
||||
|
||||
crates/ely-files/src/session.rs:307 — reload() 成功后遗漏更新新增的 loaded 标志,加载失败后的恢复仍持续拒绝保存。隔离测试执行 Session::loading(path)、fail_load(),成功读取文件并调用 reload(),得到 Saved;随后 edit() 得到 Dirty,begin_save() 却返回 LoadFailed。重新加载成功的会话因此无法通过保存流程持久化后续编辑,显示状态与保存能力冲突。上一轮“加载失败后外部变更仍拒绝覆盖”已通过,测试遗漏成功重载后的恢复。修复方向:成功重载同步更新加载生命周期与内容可用性,失败时保持原状态,并补充加载失败、成功重载、编辑及保存的回归。
|
||||
|
||||
crates/ely-files/src/session.rs:443 — 身份查询错误在登记新会话之后传播,open() 返回失败时遗留虚假登记。上一轮权限错误暴露测试已通过;macOS 隔离测试依次打开 first.md 为 Fresh(0)、restricted/second.md 为 Fresh(1),撤销 restricted 的访问权限,再执行 open(first.md, true),得到 PermissionDenied。恢复权限并关闭会话 0 后,is_open(first.md) 仍为 true,再次打开得到 Focus(2),该会话从未成功创建,无法聚焦有效窗口。修复方向:全部可能失败的身份查询在提交登记前完成,失败时保持登记与编号原状;补充已匹配文件后仍有权限失败条目的只读打开回归。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:263 — 保存任务身份仅由 generation 与 revision 组成,同一修订的两次保存会得到相同身份。隔离测试先保存到 first.md,随后保持修订号启动另存为 second.md;第一项任务的重复完成回调实际返回 Ok(()),将状态提前改为 Saved 并释放第二项任务的槽位。second.md 写入成功后的真实完成回调返回 StaleTicket,会话路径仍停留在 first.md,后续保存会继续指向旧文件。上一轮跨 reload 的重复回调场景已通过,同一 generation/revision 的重叠场景仍失败。修复方向:每次保存分配独立任务身份,完成回调按任务身份核对槽位,保留修订号与加载代次的内容有效性检查;补充同修订连续另存为及旧回调重放的回归。
|
||||
|
||||
crates/ely-files/src/session.rs:296 — fail_save() 缺少票据参数,仍无条件清空当前保存槽位,成功回调新增的身份守卫未覆盖失败回调。隔离测试执行“任务 A 开始、A 失败、继续编辑、任务 B 开始、A 的失败回调重复到达、B 写入成功并完成”,重复失败回调将状态改为 SaveFailed,B 的 finish_save() 返回 StaleTicket;B 进行期间的并发保存守卫也被解除。修复方向:失败回调携带独立任务身份,仅由匹配任务释放槽位并更新状态;补充旧失败回调与新任务重叠的回归。
|
||||
|
||||
crates/ely-files/src/session.rs:436 — 已找到需要聚焦的会话后仍继续查询其他登记路径,后续无关路径的权限错误会阻断同文件聚焦,违反 §6.1。macOS 隔离测试依次打开 first.md 为 Fresh(0)、restricted/second.md 为 Fresh(1),撤销 restricted 目录访问权限,再打开可正常访问的 first.md;is_open(first.md) 返回 Ok(true),open(first.md, false) 却返回 PermissionDenied。上一轮只读打开失败后保持登记与编号不变的回归已通过。修复方向:普通重复打开确认匹配后立即返回 Focus;新建登记继续保持失败时无副作用,并增加已匹配文件后存在无关权限错误条目的聚焦回归。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:299 — fail_save() 新增的身份校验仅比较 task,遗漏重新加载后的 generation 校验,旧内容的保存失败会污染当前会话状态。隔离测试执行“编辑、开始保存 A、外部修改、成功 reload、A 的失败回调到达”,reload 后状态为 Saved、修订号为 0,fail_save(A) 实际返回 Ok(()) 并将状态改成 SaveFailed,违反 FILE-02 的当前文件状态要求。上轮旧失败回调与新任务重叠的回归已通过;本轮跨 reload 的失败回调探针失败。修复方向:匹配任务释放槽位后核对加载代次,过期失败返回 StaleTicket 并保留当前状态;补充重新加载后旧任务失败及后续保存恢复的回归。
|
||||
|
||||
crates/ely-files/src/session.rs:445 — 同文件聚焦仍受登记顺序影响:匹配目标前遇到无关路径的权限错误就提前返回,违反 §6.1。macOS 隔离测试先打开 restricted/first.md 为 Fresh(0),再打开 second.md 为 Fresh(1),撤销 restricted 目录访问权限;second.md 仍能正常读取,open(second.md, false) 与 is_open(second.md) 却均返回 PermissionDenied。上轮目标排在错误条目前的场景已通过,调换登记顺序后复现失败。修复方向:在传播其他条目的身份查询错误前完成已有会话匹配,保留确实无法判定时的错误与登记原子性;补充无关权限错误条目排在目标前后的聚焦回归。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:172 — finish_load() 与第 189 行 fail_load() 缺少加载任务有效性校验,旧加载回调会污染成功 reload 后的当前会话,违反 FILE-02 与 §6.1。两项隔离测试分别执行 Session::loading(path)、外部修改、reload() 成功、edit(),再投递初始加载的延迟成功或失败回调:成功回调将 Dirty 改成 Saved,此时 revision=1、saved_revision=0,记录的指纹长度从新版本的 20 字节退回旧版本的 7 字节;失败回调将状态改成 LoadFailed,begin_save() 返回 LoadFailed,阻止当前有效内容保存。现有 31 项测试通过,两项延迟加载探针均失败。修复方向:加载成功与失败回调只允许更新仍有效的加载任务,reload 使旧加载失效;补充重载并编辑后旧加载成功、失败回调的回归,验证状态、指纹和后续保存能力。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/session.rs:501 — 文件共 502 行,违反 agents.md 第 5 条“文件不超过 500 行”。在项目根目录运行 `wc -l crates/ely-files/src/session.rs` 即返回 502。修复方向:清理文件内已无调用的代码(例如第 374–379 行的私有 `Key` 类型 `PartialEq` 实现),将文件控制在 500 行以内。
|
||||
@@ -0,0 +1,17 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/save.rs:40 — [P1] 固定 `.bak` 路径直接交给 `fs::copy`,会截断已有目标并跟随链接,违反 FILE-04 与 §6.1 的旧文件保留要求。macOS 实测:创建 `report.md.bak` 为 `report.md` 的硬链接,把目录设为 0555,调用 `save_with_backup`;复制先将原文件清空,随后创建临时文件返回 PermissionDenied,保存失败后原文件已变成零字节。目录可写时函数返回成功,但备份仍是零字节;`.bak` 指向其他文档的符号链接也会导致该文档被覆盖。修复方向:以排他创建方式取得独立备份文件,处理已有名称和链接冲突,验证备份完成后再替换目标;增加上述失败路径回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:17 — [P1] 保存路径没有执行 FILE-05 要求的外部修改检查。`Session::external_changed` 只有测试调用,`begin_save` 仅检查手动设置的冲突标记,`save_atomic` 与 `save_with_backup` 直接写入。实测:加载文件、产生本地编辑、由外部改写文件,在文件监听通知到达前执行 `begin_save → save_atomic → finish_save`,外部内容被覆盖,会话进入 Saved。修复方向:将加载指纹与保存目标绑定,在保存流程内检查并传播检查错误和冲突,冲突时保留两个版本供重新加载、保留本地副本或比较;回归测试须覆盖尚未收到外部通知的保存。
|
||||
|
||||
crates/ely-files/src/save.rs:61 — [P1] 导出防覆盖只比较路径文本,同一文件的路径别名可以绕过检查,违反 FILE-06。实测:正在编辑 `/tmp/.../report.md`,创建 `child` 目录,将导出目标设为 `/tmp/.../child/../report.md`;`export` 返回成功,源文件内容变成 `%PDF-export`。修复方向:按实际解析后的目标路径和文件身份校验是否与编辑源文件重合,保留路径解析错误,并增加父目录别名、符号链接目录及大小写别名的覆盖测试。
|
||||
|
||||
crates/ely-files/src/save.rs:19 — [P2] 原子替换直接使用临时文件的权限,保存会改变已有文档的访问权限。macOS 实测:将已有文件权限设为 0640,执行 `save_atomic` 成功后权限变成 0600,原来通过组权限读取该文档的用户失去访问权。修复方向:替换前保留原目标的适用权限和访问控制属性,将复制失败作为保存错误处理;增加保存前后访问权限一致的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:48 — [P2] 备份名称无条件在完整文件名后追加 `.bak`,使合法长文件名无法保存,违反 FILE-09。实测:先创建文件名为 250 个 `x` 加 `.md` 的文件,普通文件写入成功,`save_with_backup` 因生成 257 字节的文件名返回 InvalidFilename / File name too long。修复方向:为内部备份生成符合目标文件系统长度限制的独立名称,覆盖接近文件名上限的保存测试。
|
||||
|
||||
crates/ely-files/src/save.rs:89 — [P2] 附件重名处理直接追加数字后缀,也会把合法文件名扩展到文件系统上限之外,违反 FILE-09。实测:文件名为 251 个 `x` 加 `.png`,首次导入成功,第二次导入生成 257 字节的 `...-1.png`,返回 InvalidFilename。修复方向:在生成重名目标时预留后缀空间并安全处理非 ASCII 文件名,增加长文件名重复导入测试。
|
||||
|
||||
crates/ely-files/src/save.rs:108 — [P2] 单独文件名的 `Path::parent()` 返回空路径,`unwrap_or(".")` 保留了这个空值,随后 `statfs("")` 失败。macOS 实测:在有效工作目录调用 `atomic_capable(Path::new("report.md"))`,返回 NotFound / No such file or directory,导致合法相对目标无法完成保存能力检查。修复方向:将无目录分量的目标解析到当前目录,再探测对应存储介质;增加单独相对文件名测试。
|
||||
|
||||
crates/ely-files/src/session.rs:501 — [P2] 文件实测为 501 行,超过 agents.md 规则 5 的 500 行上限。修复方向:将该文件收至 500 行以内,并在检查中覆盖此限制。
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/save.rs:253 — [P1] 新增的统一保存入口始终调用 `save_atomic`,跳过 `atomic_capable` 和 `save_with_backup`,违反 §6.1 对非本地目标保留上一份文件并提示限制的要求。隔离验证中,通过 macOS `statfs` 拦截清除 `MNT_LOCAL`,确认 `atomic_capable(target)` 返回 false;随后调用 `save`,结果仍为成功、会话进入 Saved,目录仅留下新内容的目标文件,旧版本已消失。修复方向:在统一保存流程内探测目标能力,为受限目标执行保留旧版本的保存路径,并向调用者返回限制与备份信息;覆盖能力为 false 的分支。
|
||||
|
||||
crates/ely-files/src/save.rs:73 — [P1] 上轮权限保留问题仍存在:`COPYFILE_ACL` 被写成 `1 << 2`,macOS 系统头文件中该值实际为 `COPYFILE_XATTR`,ACL 的值是 `1 << 0`。实测给 0644 文档添加 `user:nobody deny read` ACL,调用 `save_atomic` 返回成功后,该 ACL 条目消失,文件访问限制被放宽。现有测试仅核对 mode 位。修复方向:使用正确的 ACL 标志并传播复制失败,增加保存前后 ACL 一致的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:133 — [P1] 新备份按默认创建权限落盘,遗漏源文件的访问限制。实测在 `/private/tmp` 下的 0755 目录创建 0600 文档,在 umask 022 下执行 `save_with_backup`,得到的备份权限为 0644,其他本机用户因此可以读取原本受保护的文档内容。修复方向:排他创建备份时先限制访问,在复制内容前应用源文件适用的权限和 ACL,任何失败均传播;增加共享目录中的私有文件备份回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:63 — [P1] 保存通过符号链接加载的文档时,`persist(path)` 替换链接本身,使会话与实际加载的文件分离。实测创建 `alias.md -> report.md`,通过 `Session::load(alias.md)` 加载并编辑,再执行 `save`;函数成功且状态为 Saved,`alias.md` 已变为普通文件,`report.md` 仍为旧内容。后续从原文件路径打开时看不到已保存的编辑。修复方向:将保存目标解析到实际加载文件,在该目标目录暂存并替换,保留链接关系,同时让外部修改检查针对同一目标;补充经文件符号链接保存的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:126 — [P2] 备份名称只有 100 个候选,而每次成功保存都会永久占用一个,导致正常重复保存达到固定上限后持续失败。实测对同一 `report.md` 连续调用 `save_with_backup`,前 100 次成功,第 101 次返回 AlreadyExists / `no free backup name`;目录仍可写且有可用空间。修复方向:使用能够持续生成排他备份名称的策略,保持已有文件保护,并增加超过 100 次连续保存的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:30 — [P2] 上轮附件长文件名问题仍有遗漏:长度预算只截断 stem,suffix 超过 NAME_MAX 时仍被完整追加。实测源文件名为 `a.` 加 253 个 `x`,共 255 字节;首次导入成功,第二次生成 `-1.` 加 253 个 `x`,共 256 字节,返回 InvalidFilename / File name too long。修复方向:对完整候选名统一分配长度预算,覆盖扩展名本身接近上限的情况,并增加长扩展名重复导入测试。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/save.rs:307 — [P1] 能力探测使用传入路径的父目录,实际保存却在 `resolved(path)` 指向的目录替换文件;经本地符号链接保存网络盘文件会跳过旧版本备份,违反 §6.1。macOS 隔离探针仅对目标目录的 `statfs` 清除 `MNT_LOCAL`:直接保存目标返回备份且保留旧内容;经本地目录中的符号链接保存同类目标时,`atomic_capable(alias)` 返回 true,`save` 返回 `backup=None`,会话进入 Saved,目标旧内容消失。修复方向:能力探测、备份与替换统一使用解析后的实际目标,并增加本地链接指向受限卷的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:130 — [P2] 备份名在追加唯一后缀后整体截断,截掉区分候选名的部分,导致合法长文件名的保存卡在碰撞循环,违反 FILE-09。实测 255 个 `x` 的文件首次调用 `save_with_backup` 就持续尝试原文件名,3 秒超时后终止;250 个 `x` 加 `.md` 的文件前十次保存成功,第十一次生成的名称又与 `-1` 候选相同,同样超时。现有长文件名测试仅保存一次,105 次测试仅使用短文件名。修复方向:按完整唯一后缀预留长度,再截断原名称,保证每次碰撞产生新候选;补充满长名称首次保存和长名称连续保存超过十次的回归测试。
|
||||
|
||||
crates/ely-files/src/save.rs:219 — [P2] 附件重名候选也在追加计数器后整体截断,长 stem 会吞掉计数器,使重复导入持续检查同一已存在路径,违反 FILE-09。实测无扩展名、名称为 255 个 `x` 的附件首次导入成功,第二次导入卡在 `while target.try_exists()`,3 秒超时后终止;源文件和目录均可正常读写。现有长扩展名测试通过,但没有覆盖满长 stem。修复方向:为计数器保留长度预算,使截断后的候选仍随计数器变化,并补充满长无扩展名附件的重复导入测试。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/save/tests.rs:364 — [P2] 新增符号链接回归直接向 `save_mode` 传入真实路径和 `backup=true`,且链接与目标位于同一目录,覆盖不到上一轮出错的 `save → resolved → atomic_capable` 流程。上一轮三个行为缺陷已分别复验通过,但在临时副本中仅将 `save` 的 `atomic_capable(&target)` 回退为 `atomic_capable(&path)`,全部 58 项测试仍通过;独立 macOS 探针只对另一目录的真实目标清除 `MNT_LOCAL`,经本地链接保存时随即复现 `backup=None`、状态为 Saved、旧版本丢失。修复方向:通过公开 `save` 入口测试跨目录链接,并让能力探测仅对真实目标目录返回受限;断言备份的位置及旧内容,确保该单行回退会使回归测试失败。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,17 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/recovery.rs:207 — [P1] `restore_copy` 使用 `std::fs::copy`,会截断已有目标并跟随目标符号链接,违反 §6.3 的恢复生成新副本要求。实测将包含 `recovered` 的恢复点复制到已有 `source.md`,或指向该文件的 `restored.md` 符号链接,均返回 `Ok(())`,原内容 `original` 被覆盖。修复方向:排他创建恢复目标,目标已存在时返回错误或选择新名称,保证路径别名也无法覆盖源文件;补已有文件、符号链接与硬链接回归测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:111 — [P1] 恢复内容直接写入正式 `.bin`,枚举时缺少完整提交校验,失败写入会成为可恢复内容。子进程设置 `RLIMIT_FSIZE=1024` 后写入 8192 字节,`write_once` 返回错误、`last_ok` 为 `None`,重建 `Recovery` 后却列出长度仅 1024 的恢复点。清理也有同类中断问题:在删除 `.bin` 后留下 `.cursor`,`points()` 会返回不存在的内容路径,后续写入在 `bytes()` 中持续报 `NotFound`。修复方向:暂存并校验、同步完整恢复点后原子发布,枚举只认可已提交内容,并让中断清理可恢复;补部分写入及清理中断测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:92 — [P1] `last_ok` 在清理前更新,清理能够删除全部恢复点,随后仍返回成功及已删除路径,违反 §6.2 对最后成功恢复时间和持久化状态的要求。实测容量为 4 字节,先写入 `old!`,再写入 `large`:两份内容全部被删除,第二次结果仍为 `Ok(Some(path))`,`last_ok` 变成第二次时间,`points()` 为空;默认容量下单个文档超过 64 MiB 同样触发。修复方向:提交前核算单点容量,超限明确失败并保留最近有效恢复点,最终确认恢复点仍可用后再更新成功状态;补超大单点测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:267 — [P1] 放弃状态只记录在随后会被 `Crash::open` 覆盖的 `session.lock` 中,恢复点没有会话归属或持久放弃状态。实测先生成 `explicitly discarded` 并调用 `discard()`,再由新子进程打开同一文档目录并在首次恢复写入前遭到 SIGKILL;下次检查返回 `Crashed`,恢复列表重新包含已放弃内容,违反 §6.2。修复方向:将放弃结果与对应会话或恢复点持久关联,保证下一次会话开启及崩溃后仍排除该内容;补真实进程终止回归测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:75 — [P1] 加密文档恢复被无条件拒绝,§18.2 要求的加密恢复与系统密钥保护路径缺失。`Recovery::sensitive(...).write_once(...)` 对所有输入直接返回 `PermissionDenied`,代码和依赖均没有获取恢复密钥、加密写入或解密恢复的入口,因此已解锁的加密文档也会永久失去崩溃恢复能力。现有测试仅验证拒绝写入。修复方向:接入成熟加密库及系统密钥保护,验证加密恢复往返、磁盘内容保密和密钥无法解锁时的明确失败。
|
||||
|
||||
crates/ely-files/src/recovery.rs:31 — [P2] 10 秒间隔仅为未被执行路径使用的常量,模块提供的写入入口同步完成全部 I/O,缺少 §6.2 要求的后台定时恢复调度。源码检索确认没有调度调用方;实测在时间 100 秒和 101 秒连续调用会立即产生两个恢复点。`running` 仅在同一个持有独占 `&mut self` 的同步调用内设为真再清零,现有并发测试直接篡改私有字段,未验证实际后台任务重叠。修复方向:实现可执行的 10 秒后台调度及同文档任务占用管理,以可控时钟和延迟写入验证周期、跳过重叠任务及失败后继续调度。
|
||||
|
||||
crates/ely-files/src/recovery.rs:120 — [P2] 七天清理仅由新恢复点写入触发,重启与读取恢复列表均保留过期副本,违反 §6.2 的最长保留时间。实测写入时间为当前时间减八天的恢复点,随后销毁并重新创建 `Recovery`;`points()` 仍返回该内容,磁盘文件仍存在。用户只查看恢复列表或文档之后没有新编辑时,副本会无限期保留。修复方向:在恢复目录打开、扫描或维护流程中按当前时间执行过期清理,并测试重启后没有新写入的情况。
|
||||
|
||||
crates/ely-files/src/recovery.rs:182 — [P2] 容量统计只累计 `.bin`,光标与历史文件完全漏算,实际恢复目录可突破用户设置的上限。实测容量为 4 字节,写入 1 字节内容及 4096 字节光标字符串后返回成功,目录实际占用 4097 字节。修复方向:容量核算覆盖每个恢复点的全部文件,以带元数据的恢复点验证容量清理和超限失败。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:61 — [P2] r9 的账户查询并发缺陷仍存在:`getpwuid` 返回共享静态存储,随后读取 `pw_dir` 时另一线程可覆盖它。macOS 构建及 89 项测试通过;Linux 隔离副本使用项目锁文件中的同版本依赖,普通账户 `reviewer` 下完整测试首次通过,重复运行现有 `key::tests::the_account_home_holds_under_concurrent_queries` 时第 8 次退出 101,`key/tests.rs:251` 实际目录为 `/root`,期望为 `/home/reviewer`。这会把 `RecoveryKey::load()` 的锁目录切到 `/root/.local/share/ElyOffice`,普通账户随即因权限错误无法加载恢复密钥,阻断加密文档恢复(PRD §18.2)。修复方向:使用调用者独占缓冲区的 `getpwuid_r`,显式处理缓冲区不足、查询失败和账户缺失,并在 Linux 普通账户下验证并发查询回归。r9 的用户名硬编码问题已修复,修改 HOME 的子进程测试在两平台均通过。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,17 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/recovery.rs:145 — [P1] 加密恢复点分两次发布 `.enc` 和 `.nonce`,枚举却只凭 `.enc` 认定恢复点存在,上一轮的完整提交缺陷仍存在于加密路径。隔离探针让 nonce 写入失败,留下已发布密文;移除故障后,`points()` 与后续 `write_once()` 均持续返回 `NotFound`,连已有有效恢复点也无法列出。第 258 行先删 nonce 再删密文,清理中断同样复现。修复方向:以完整恢复点为单位原子提交,枚举识别提交状态,清理中断保持可恢复,并覆盖发布与删除各阶段的故障测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:107 — [P1] 容量预检遗漏加密包的 4 字节长度头、16 字节认证标签和 12 字节 nonce,上一轮“清空全部恢复点后仍报告成功”仍可复现。容量设为 40 字节,先写入 `old!`,再写入 `123456789`:第二点实际占 41 字节,清理删除两点,结果却为 `Ok(Some(path))`,`last_ok` 更新且 `points()` 为空。修复方向:按实际完整落盘大小做提交前容量校验,超限保留旧点,确认新点仍可恢复后才更新成功状态;补加密容量边界回归。
|
||||
|
||||
crates/ely-files/src/recovery.rs:53 — [P1] `open()` 使用默认 64 MiB 容量先清理,再允许调用方通过 `with_limits` 应用用户设置,会删除用户容量范围内的有效恢复点。实测以 70 MiB 上限写入 65 MiB 内容,随后执行 `Recovery::open(dir, now)?.with_limits(20, 70 MiB)`,恢复列表变空、文件已删除。修复方向:打开和清理前接收用户容量设置,保证重启前后的保留策略一致,并补自定义容量重开测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:419 — [P1] 系统恢复密钥的读取与创建存在覆盖竞态,且所有读取错误都进入生成新密钥的分支。两个应用首次同时读取同一 `SERVICE/ACCOUNT`,均读到缺失后各自生成密钥;所用 `set_generic_password` 会更新已有项,因此最后只保留一把密钥。对原样抽取的 `RecoveryKey::load` 使用隔离 Keychain 替身和屏障实测,两次调用均成功、返回密钥不同,其中一个应用生成的恢复点在使用持久密钥重开时返回 `InvalidData`。另注入读取 `PermissionDenied`,后续写入可成功时会直接替换旧密钥。修复方向:只对明确的条目缺失创建密钥,采用排他创建,竞态失败后读取已建立的密钥;其他读取错误原样暴露。补并发初始化及拒绝访问回归。
|
||||
|
||||
crates/ely-files/src/recovery.rs:441 — [P1] 上一轮要求的系统密钥保护路径在 Windows、Linux 仍为空实现,`RecoveryKey::load()` 无条件返回 `Unsupported`。两平台即使系统凭据库可用、文档已解锁,也无法通过当前接口建立可跨崩溃恢复的受保护密钥,违反 §18.2。Windows、Linux 编译检查通过仅证明该拒绝分支可编译。修复方向:接入 Windows 系统凭据能力和 Linux Secret Service,并验证跨进程密钥读取、加密恢复往返及凭据库不可用时的明确失败。
|
||||
|
||||
crates/ely-files/src/recovery.rs:80 — [P2] 上一轮的后台调度缺陷仍存在:新增 `tick()` 仅返回到期布尔值,全项目只有测试调用;全部写入和清理仍同步完成,缺少实际执行的 10 秒后台恢复任务。重放时 `tick(100)` 本身没有写入,时间 100 秒与 101 秒的两次 `write_once` 仍立即产生两点;`running` 仍只在持有独占 `&mut self` 的同步调用内变化,并发测试继续直接修改私有字段。修复方向:落实后台周期调度及同文档任务占用管理,以可控时钟和延迟写入验证真实调度、重叠跳过及失败后的继续执行。
|
||||
|
||||
crates/ely-files/src/recovery.rs:60 — [P2] 七天保留修复仅覆盖普通 `open()`;加密入口 `sensitive()` 和恢复列表读取均跳过过期清理,上一轮的超期保留场景仍成立。实测写入时间为当前时间减八天的加密恢复点,销毁后以同一密钥重新创建 `Recovery::sensitive`,`points()` 仍列出该点且文件仍在;已打开后长期没有新写入的目录也缺少维护路径。修复方向:在加密与普通恢复的打开、扫描或维护流程统一执行年龄限制,并补无新写入的加密重启与持续打开测试。
|
||||
|
||||
crates/ely-files/src/recovery.rs:194 — [P2] 加密点把缺失光标编码为空串,解密枚举又无条件包装为 `Some`,丢失 §6.3 要求的恢复能力区分。实测 `Recovery::sensitive(...).write_once(content, None, time)` 后,`points()[0].cursor` 为 `Some("")`;上层据此判断有光标恢复数据时会得到虚假状态。修复方向:在加密包中保留光标是否存在的信息,完整往返 `None` 与 `Some`,补未提供光标时的回归。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/recovery.rs:107 — [P1] 加密容量预检遗漏第 143 行新增的 1 字节光标存在标志,上轮容量缺陷仍可导致全部恢复点丢失。隔离实测:容量设为 40 字节,先写入 `old!`,再写入 `12345678`,第二点实际占 41 字节;清理删除两点,`write_once` 却返回 `Ok(Some(path))`,`last_ok` 更新到第二次时间,`points()` 为空。现有 76 项测试全部通过,新增容量边界断言失败。修复方向:统一按完整序列化结果核算容量,提交前拒绝超限点并保留旧点,确认新点仍存在后更新成功状态;补加密容量边界回归。
|
||||
|
||||
crates/ely-files/src/recovery.rs:436 — [P1] 密钥创建仍使用会覆盖已有凭据的 `set_secret`,随后重读无法消除上轮初始化竞态。通过 keyring 可注入凭据后端与屏障复验:两个调用先同时读到 `NoEntry`,A 写入并重读返回后,B 再写入并重读;两次均成功,返回密钥不同,持久凭据只保留 B 的密钥。A 生成的恢复点使用持久密钥重开时返回 `InvalidData`。macOS 后端明确采用覆盖已有项的语义;拒绝读取时覆盖旧密钥的分支已修复。修复方向:对跨进程初始化采用排他创建或锁内重查,保证成功返回的密钥持续有效;补上述交错顺序与跨实例解密回归。
|
||||
|
||||
crates/ely-files/src/task.rs:69 — [P1] `RecoveryTask::drop` 只记录日志,后台线程被分离后继续持有恢复状态和快照闭包,能够撤销用户的显式放弃。隔离实测:首点写入后暂停下一次快照,销毁任务句柄,再对同一目录调用 `Recovery::discard()`;此时标记存在、恢复列表为空。释放快照后,遗留线程重新生成恢复点并清除 `.discarded`,实测得到 `discarded_marker=false`、`points_after_discard=1`,违反 §6.2。修复方向:任务销毁时可靠取消并结束工作线程,正在生成快照的任务在提交前复查取消状态,保证销毁完成后再无写入;补销毁、显式放弃和延迟快照的回归。
|
||||
|
||||
crates/ely-files/src/task.rs:57 — [P2] `last_ok()` 与整次同步写入、清理共用同一互斥锁;后台 I/O 阻塞期间,调用方也无法取得上一份成功恢复时间,违反 §6.2 对大文件未完成时显示该时间的要求。临时副本仅在 `save_atomic` 注入 800 毫秒 I/O 延迟,已有成功恢复点时查询 `last_ok()` 仍阻塞约 817 毫秒,直到新写入完成才返回;真实慢盘或挂起写入会同样阻塞状态查询线程。修复方向:让已完成状态的读取独立于进行中的磁盘 I/O,写入完成后发布新状态;补延迟写入期间可立即读取旧成功时间的回归。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:71 — [P1] 取得初始化锁后直接调用 `init`,缺少锁内重读,上轮密钥覆盖竞态仍成立。隔离后端实测:B 首次读取已取得 `None` 后暂停,A 完成初始化并返回,随后 B 继续、取得已释放的锁并覆盖凭据;两次调用均成功且密钥不同,A 的恢复点用最终持久密钥恢复时返回 `InvalidData`。原有 81 项测试全部通过,受控交错回归失败。修复方向:在取得排他锁后重新读取凭据,仅在锁内确认缺失时生成密钥,并覆盖该交错顺序与跨实例解密回归。
|
||||
|
||||
crates/ely-files/src/key.rs:76 — [P1] 等待初始化超过 1 秒即删除锁,仍在运行的持有者会失去排他保护。隔离实测:让 A 的凭据写入暂停超过 1 秒,B 删除 A 的锁、写入并成功返回密钥;恢复 A 后,A 再次覆盖凭据并成功返回另一密钥,B 已生成的恢复点随即无法解密。系统凭据解锁或慢响应可触发同一顺序;取得锁后重读也无法修复这个场景。修复方向:采用随持有进程退出而释放的系统锁,存活持有者等待超时应返回明确错误;分别传播凭据读取与锁检查错误,补慢初始化并发回归。
|
||||
|
||||
crates/ely-files/src/task.rs:27 — [P2] 独立状态锁初始化为 `None`,丢失传入 `Recovery` 已有的成功时间。隔离实测:先成功 `write_once`,再以 10 秒间隔启动同一对象的后台任务,立即调用 `task.last_ok()` 得到 `None`;该状态持续到下一次写入尝试结束,大文件写入阻塞时也无法显示已有成功时间,违反 §6.2。修复方向:启动时用传入对象的 `last_ok()` 初始化发布状态,并补首次后台写入尚未完成时保留已有成功时间的回归。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:114 — [P1] 空锁文件以及读取、解析失败均被当作持有者死亡,随后删除锁;创建锁到写入 PID 之间仍存在丢失排他保护的窗口。隔离副本仅插入调度屏障复现:A 创建锁后暂停,B 读取空锁并接管、暂停在凭据写入前;A 恢复并成功生成密钥及恢复点,B 随后覆盖凭据并成功返回另一密钥。最终持久密钥恢复 A 的副本时报 `InvalidData`。现有 85 项测试全部通过,该受控交错回归失败。修复方向:使用随持有者退出释放的系统排他锁,传播锁操作错误,并覆盖尚未写入 PID 时的并发初始化与最终密钥解密回归。
|
||||
|
||||
crates/ely-files/src/key.rs:73 — [P1] 取得锁后的错误通过 `?` 提前返回,锁文件只在成功路径清理;新存活检测随后把已结束的初始化视为仍在运行。隔离实测:后端首次写入返回 `PermissionDenied`,模拟取消系统凭据解锁;后端恢复可用后,同一进程再次调用仍返回 `TimedOut`,锁文件保留,后续尝试持续失败。加密文档的恢复初始化因此要等持有进程退出才能恢复。修复方向:用作用域锁守卫覆盖所有成功与错误出口,补暂时读取或写入失败后原进程重试成功的回归。
|
||||
|
||||
crates/ely-files/src/key.rs:136 — [P1] `#[cfg(unix)]` 分支使用 macOS 的 `libc::__error()`,Linux 的 libc 接口没有该函数,导致 Linux 编译失败。将该函数原样提取,使用项目锁定的 libc 0.2.189 和 Rust 1.96.0 对 `x86_64-unknown-linux-gnu` 编译,实际报 `E0425: cannot find function __error in crate libc`;整 crate 的 Linux 检查另受本机缺少 libdbus 交叉编译环境阻挡。修复方向:使用标准库读取系统错误,或按操作系统选择 errno 接口,并加入 Linux 目标编译检查。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:60 — [P1] 初始化锁放在调用方传入的目录,系统凭据却固定为第 43 行的全局 `ElyOffice Recovery/recovery-key`;不同目录的锁无法互斥保护同一凭据。两个应用使用各自数据目录首次初始化时,可各自取得锁并在锁内读到缺失,随后依次覆盖全局密钥。隔离后端加屏障实测:A、B 两次初始化均成功且返回不同密钥;A 返回后生成加密恢复点,B 随后完成初始化,最终持久密钥恢复 A 的副本返回 `InvalidData: the sealed point is unreadable`。原有 85 项测试全部通过,跨应用目录的受控交错回归失败,违反 §18.2 的加密崩溃恢复要求。修复方向:使锁与凭据采用一致的共享范围,为同一全局凭据固定唯一跨进程锁位置,或将凭据按稳定身份隔离;补不同应用目录并发初始化及最终持久密钥解密既有恢复点的回归。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:55 — [P1] 第 6 轮的锁与凭据作用域缺陷在不同数据根目录下仍成立:`BaseDirs::data_local_dir()` 在 Linux 随进程的 `XDG_DATA_HOME` 改变,第 43 行的凭据身份始终是全局 `ElyOffice Recovery/recovery-key`。同一用户在同一桌面会话中用不同 `XDG_DATA_HOME` 启动两个应用并首次初始化时,两者取得不同锁,能够覆盖同一系统凭据。Linux 容器构建后,以共享的隔离凭据后端调用未修改的 `RecoveryKey::load()`,受控交错实测两次加载均成功、返回密钥不同;A 先生成的加密恢复点用最终持久密钥枚举时报 `InvalidData: the sealed point is unreadable`。相同数据根目录的对照通过。修复方向:将锁和凭据绑定到同一稳定身份,使凭据命名空间随数据根目录隔离,或为同一系统凭据选择独立于进程数据目录配置的共同锁;补不同 `XDG_DATA_HOME` 的跨进程初始化和最终密钥解密回归。
|
||||
|
||||
crates/ely-files/src/key.rs:65 — [P1] 锁前读取可将尚未持久化的密钥成功返回给另一个应用。当前 Linux 配置 `linux-native-sync-persistent` 的 keyring 3.6.3 后端先写共享 keyutils 缓存,再写 Secret Service;后一步失败时会删除刚建的缓存。A 持锁初始化、密钥已进入缓存而 Secret Service 尚未完成时,B 在这里读取缓存并直接返回,绕过初始化锁;A 随后失败回滚,B 已生成的恢复点失去可重新加载的密钥。按该依赖的实际发布与回滚顺序构造隔离后端,通过公开入口实测:B 成功返回并写入加密恢复点,A 初始化失败后密钥消失,重试产生的新持久密钥读取该点报 `InvalidData`。临时副本将读取移入锁内后,同一探针通过;原有 85 项测试全部通过。修复方向:让读取等待初始化事务结束,并以持久凭据后端的完成结果判定密钥可用;补初始化中途可见、持久化失败回滚及随后重试的并发回归。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:55 — [P1] 共同锁仍依赖进程环境中的 `HOME`,凭据身份则固定为 `ElyOffice Recovery/recovery-key`;`directories` 6.0.0 在 macOS/Linux 的 `home_dir()` 优先读取 `HOME`,同一系统用户、同一凭据服务下的两个应用使用不同 `HOME` 时会取得不同锁,并发覆盖同一恢复密钥。隔离双进程探针调用原样的 `RecoveryKey::load()`,仅以共享测试凭据后端控制首次读取交错:A、B 都读到缺失,A 完成初始化并生成加密恢复点,B 随后覆盖凭据;两次加载均成功且密钥不同,最终持久密钥读取 A 的恢复点返回 `InvalidData: the sealed point is unreadable`。macOS 与 Linux 均复现;相同 `HOME`、不同 `XDG_DATA_HOME` 的对照及 r7 的失败回滚回放均通过。现有 macOS 87 项、Linux 86 项测试及 Windows 目标编译检查通过,新增路径单测只检查传入常量的拼接,未覆盖实际账户与环境变量的作用域差异。修复方向:用稳定的操作系统账户身份定位共同锁,或将锁与凭据绑定到同一规范化命名空间;补同一账户、不同 `HOME` 的跨进程初始化及最终持久密钥解密既有恢复点的回归。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/key.rs:61 — [P2] 新增账户查询通过 `getpwuid` 取得共享静态存储,随后在同步保护之外读取 `pw_dir`;Linux 上另一线程的账户查询可改写该存储,使恢复密钥锁目录漂移。普通账户 `/home/reviewer` 下,一线程执行原样的 `account_home()`,另一线程调用 `getpwuid(0)`,第 6 次查询即返回 `/root`;通过原样公开入口 `RecoveryKey::load()` 与隔离凭据后端回放,第 9627 次加载出现 `PermissionDenied`,已有恢复密钥因此加载失败。临时副本改用调用者持有缓冲区的 `getpwuid_r` 后,同一并发探针 20000 次加载通过。修复方向:采用可重入账户查询,处理缓冲区不足、查询失败与账户缺失,并补并发查询其他账户时锁目录始终属于当前账户的回归。r8 的不同 `HOME`/`XDG_DATA_HOME` 双进程初始化回放已通过,两份恢复点均可用最终持久密钥解密。
|
||||
|
||||
crates/ely-files/src/key/tests.rs:241 — [P2] 新增 Unix 回归测试将系统账户目录硬编码为 `/Users/zacharyzhang`,使其他账户及 Linux 的正确实现必然测试失败。macOS 本机 88 项测试通过;Linux 同版本依赖构建后,以普通账户运行得到 86 项通过、1 项失败,此处实际值为 `/home/reviewer`,期望值为 `/Users/zacharyzhang`。该测试也没有改变 `HOME`,无法锁住 r8 的环境覆盖场景。修复方向:从当前测试账户取得基准,在独立子进程中分别设置不同 `HOME`,验证账户目录和共同锁保持一致,并验证最终持久密钥能解密各进程的恢复点。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-files/src/policy.rs:102 — 保存完成只接收 `ok: bool`,成功时无条件清除最新编辑的计时,丢失保存期间新增修订的自动保存任务。复现:已有路径的 Mark 文件在 t=100 编辑 revision 1,t=101 返回 `SaveDue` 并取得保存票据;t=101.2 编辑 revision 2;随后 revision 1 落盘,`Session::finish_save` 正确保留 `Dirty`,但 `PolicyEngine::save_finished(true)` 清空计时,t=102.2 及之后持续返回 `Nothing`,磁盘停留在 revision 1。临时测试结合 `Session` 与 `save_atomic` 实测失败,违反 §6.2 的停输入 1 秒自动保存要求。修复方向:保存完成关联提交时的修订或编辑代次,仅清除该任务已经持久化的编辑;保留较新编辑的截止时间,并加入保存期间继续输入的回归测试。
|
||||
|
||||
crates/ely-files/src/policy.rs:64 — 无路径期间的编辑被直接丢弃,首次保存成功后 `set_has_path()` 也没有恢复待保存编辑的计时。复现:新建 Mark 文件,revision 1 开始首次显式保存;保存期间输入 revision 2,`edit()` 因 `has_path=false` 跳过记录;revision 1 落盘后设置路径,此时会话仍为 `Dirty`,后续 `tick()` 永远返回 `Nothing`。临时落盘测试实测失败;即使上一项修复为保留较新的计时,此处仍没有可保留的记录。修复方向:无路径期间也记录最新编辑和修订,仅阻止自动提交;首次保存取得路径后,为尚未持久化的编辑继续调度,并覆盖首次保存期间输入的测试。
|
||||
|
||||
crates/ely-files/src/policy.rs:74 — 防抖使用可回拨的 `SystemTime`,且把负时间差静默替换为零,导致 Mark 的 1 秒自动保存被系统校时推迟。复现:`edit(UNIX_EPOCH + 100s)` 后系统时钟回拨 60 秒,实际已经停止输入 1 秒时调用 `tick(UNIX_EPOCH + 41s)`,实测返回 `Nothing`;直到墙上时间追到 101 秒才返回 `SaveDue`,实际等待约 61 秒。修复方向:用 `Instant` 或可注入的单调时钟计算防抖期限,移除吞掉时间错误的默认值,并增加校时场景的回归覆盖。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-office/src/open.rs:229 — 恢复出的敏感文档丢失封存属性。`Source::Copy` 在第 163 行把 `tried` 设为 false,`reopening` 也只携带路径、时间和名称,因此新标签以 `sealed:false` 登记。实测:用固定测试密钥替代钥匙串读取,给原生 ElyDoc 提供含 `C065_SENSITIVE_SECRET` 的有效 `.enc` 恢复点;恢复后的下一轮生成 `.bin`,新 index 全为 `sealed:false`,直接解压该文件即可读取正文(/tmp/c065-sensitive-mock-result.log)。违反 C06.5 的加密文档封存要求。修复方向:把原恢复点的敏感属性传到新标签,在进程恢复和引擎重开后持续使用封存恢复点。
|
||||
|
||||
crates/ely-office/src/recover.rs:232 — 恢复交接提前删除唯一持久副本。发起异步 `load` 后立即删除旧恢复目录,第 224、244 行同时从持久索引移除旧条目;新标签的恢复点要等下一轮计时才生成,载入失败也失去原索引。实测:第一次启动从恢复点显示 `C065_UNSAVED`,此时恢复点数量为 0;立即再次 SIGKILL 并完整等待下一次启动,原内容消失,只剩新的空白标签(/tmp/c065-double-crash2-result.log、/tmp/c065-double-crash2.log)。违反 §6.2、§6.3 的持久恢复要求。修复方向:在新标签已有持久恢复点或用户明确处理该副本之后交接旧目录和索引,载入及解封失败保留原恢复条目;引擎重开的释放路径应用同一交接规则。
|
||||
|
||||
apps/ely-pdf/src/engine.rs:135 — 加密 PDF 的恢复快照沿用普通保存,快照仍需原 PDF 密码;恢复却通过 `Source::Copy` 使用空密码,`open.rs:190` 又仅为带 registry 的文件询问密码,导致恢复副本载入失败。实测:用 `aes128.pdf` 的 owner 密码打开、旋转页面、写快照,重新打开快照得到 `locked:true`;将此快照封存为有效 `.enc` 并交给原生 ElyPDF 恢复,日志报告 `did not open: a password is needed`,最终 index 为空(固定测试密钥仅替代钥匙串,/tmp/c065-encrypted-recovery2.log、/tmp/c065-encrypted-mock-result.log)。违反 C06.5 的加密文档崩溃恢复要求。修复方向:为封存恢复生成解封后可加载的 PDF 内容,并持续携带敏感属性;覆盖加密 PDF 的进程恢复和引擎重开。
|
||||
|
||||
crates/ely-office/src/stopped.rs:18 — worker 退出检测依赖视图先发出引擎错误,空闲 ElyPDF 缺少退出通知。实测:打开 `tests/fixtures/office/t.pdf`,等待页面绘制完成,单独 SIGKILL 其 `ely-pdf-worker`,再等待 13 秒;AX 树仍保留文件标签,故障提示和 `Reopen documents` 按钮均缺失,日志也未记录引擎退出(/tmp/c065-pdf-stopped-result.log、/tmp/c065-pdf-stopped-ax.log、/tmp/c065-pdf-stopped.log)。用户无法从窗口重新打开文档,违反 §6.3 及任务所声称的引擎退出提示。修复方向:通过 worker 退出通知更新 `Stopped` 并显示最近恢复时间和重开入口,覆盖空闲 PDF 的退出场景。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-office/src/recover.rs:66 — 解封或载入失败的恢复点会在正常退出后的下一次启动被删除。`held`、`reopening` 虽保留在索引中,`end()` 仍清掉崩溃标记,随后 `start()` 第 57 行按 Clean 删除整个恢复目录。实测:提供有效封存 PDF 恢复点,钥匙串暂不可用时窗口提示恢复失败,索引和恢复点仍在;直接 Cmd+Q 正常退出,再启动时恢复点从 1 个变为 0(/tmp/c065-r2-held-result.log、/tmp/c065-r2-held-first-ax.log)。用户尚未保存或明确放弃该副本,唯一持久内容已丢失,R1 的失败保留修复仍存在此缺口。修复方向:持久记录尚待处理的恢复条目,正常退出后继续保留并重试,直到成功交接、保存或用户明确放弃。
|
||||
|
||||
crates/ely-office/src/recover.rs:216 — 恢复索引写入失败只进日志,恢复任务继续成功写点,界面隐去影响崩溃恢复的错误。实测:以空崩溃索引启动,将 `index.json` 设为 0444,打开 PDF 并旋转页面;生成 1 个 `.bin`,索引仍为 `[]`,日志报告 Permission denied,AX 树没有恢复失败提示;杀掉应用、恢复索引权限后重启,恢复点文件仍在,窗口没有恢复标签(/tmp/c065-r2-index-result.log、/tmp/c065-r2-index-first-app.log、/tmp/c065-r2-index-first-ax.log)。违反 §6.2 的恢复要求及暴露错误规则。修复方向:将索引持久化失败传回界面,保留待写条目并重试,确保报告成功的恢复点具有可供下次启动读取的持久索引。
|
||||
|
||||
crates/ely-office/src/recover.rs:291 — 接过旧恢复点时仅用 `at` 显示恢复提示,`Keeping.last` 仍为 None,故障条只读取这个值。实测:ElyPDF 从已有恢复点打开未命名副本,在下一轮快照前 SIGKILL worker;AX 树同时显示副本的恢复时间和 `Last recovery point: none yet`(/tmp/c065-r2-recovered-time-ax.log)。恢复点实际存在,§6.3 要求的最近恢复时间显示错误。修复方向:恢复交接时同步已有持久恢复点的时间,让故障条读取当前保留恢复点的实际最近成功时间,并覆盖首次恢复后立即停止引擎的场景。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-office/src/recover.rs:96 — 恢复索引直接覆写原文件,短写失败会破坏上一份有效清单,使已成功持久化的恢复点在下一次启动无法恢复,违反 §6.2、§6.3。具体复现:已有一个恢复点及有效索引,调用生产 `Keeping::save_index()`,用 `RLIMIT_FSIZE=16` 注入文件写入中途失败;调用返回 `FileTooLarge`,原索引只剩 `[{"slot":"old","`,恢复点仍有 1 个。恢复写入条件并正常退出后,`Keeping::start_in()` 返回 `InvalidData: EOF while parsing a string`;应用因第 138 行的启动错误关闭本次恢复功能,索引重试也无法在下次运行执行(/tmp/c065-r3-index-probe.log)。磁盘耗尽或进程在索引覆写期间退出同样会留下不完整索引。修复方向:先完整写入并校验暂存索引,再原子替换,失败时保留上一份有效索引,并覆盖短写与重启场景;临时副本采用暂存后替换,在同一故障下注入失败后原索引仍完整、再次启动成功(/tmp/c065-r3-index-atomic-control.log)。
|
||||
@@ -0,0 +1,21 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/Cargo.toml:17 — Windows 工作进程编译失败。实跑 `cargo check -p ely-lok --all-targets --locked --target x86_64-pc-windows-msvc`,`ffi.rs:283/314` 因缺少 `Win32_System_LibraryLoader` feature 报 E0432;`worker.rs:161/170` 无条件调用仅在 Unix 依赖中声明的 `libc::_exit`,报 E0433。补齐动态加载 feature,并为进程退出提供正确的 Windows 实现,再运行该目标检查。
|
||||
|
||||
crates/ely-lok/src/client.rs:32 — 所有平台都开启 `unipoll`,但 `worker.rs:117` 的非 macOS 分支直接读取 IPC,从未调用 `Lok::run_loop()`,内核主循环初始化缺失。Linux x86_64 隔离容器中,原代码构建成功;使用可用的 LibreOffice 7.4.7 内核核验启动模式,同一二进制在 `SAL_LOK_OPTIONS=unipoll` 下返回版本后加载 FODT 以 139 退出,清空该选项后加载成功并以 0 退出。锁定的 26.2 源码也在 unipoll 分支只调用 `InitVCL()`,将 `soffice_main()` 留给 `runLoop`。按平台接通内核循环,或在非 macOS 同步服务分支使用内核线程模式,并添加 Linux 文档加载回归测试。
|
||||
|
||||
crates/ely-lok/src/client.rs:48 — 启动握手仅记录版本,任何返回版本的内核都被接受,固定构建要求没有落实到加载边界。实测官方未打补丁的 26.2.6.3 dmg 被 `Worker::spawn` 接受;该构建与补丁版拥有相同 BuildId,却缺少本任务要求的 macOS 图块绘制能力。Linux 探针中的 7.4.7 也通过握手。加载前校验随应用发行的构建身份及补丁产物,握手核对完整版本;增加错误版本和未打补丁构建的拒绝测试。
|
||||
|
||||
crates/ely-lok/src/worker.rs:161 — 正常 SHUTDOWN 直接 `_exit(0)`,跳过文档注册表和内核清理;EOF 路径的 `quit` 同样直接退出。真实补丁内核打开 FODT 后调用 `Worker::shutdown()`,退出码为 0,但源文件旁的 `.~lock.source.fodt#`、profile 的 `.lock` 和隔离 TMPDIR 内的 `lu*.tmp` 仍存在;预先 `close_doc` 后仍留下内核临时目录。违反 LOK-10。关闭时先释放全部文档,再完成内核及其拥有的临时资源清理,覆盖显式 shutdown、Drop 和管道 EOF,并断言锁及临时文件恢复基线。
|
||||
|
||||
crates/ely-lok/src/client.rs:95 — `shutdown()` 丢弃发送与应答错误,并把任意已退出状态都返回为 `Ok(())`。注入能完成版本握手、收到 SHUTDOWN 后以 23 退出的工作进程,实测结果为 `shutdown=Ok(())`;上述真实 Linux 加载崩溃后也返回成功。校验应答和 `ExitStatus::success()`,将异常退出、协议错误及等待失败传给调用方,并覆盖这些失败路径。
|
||||
|
||||
crates/ely-lok/src/client.rs:56 — 字符串应答的解码与工作进程编码不匹配。服务端使用 `Pack::str` 写入四字节长度,客户端在此处和 `:78` 将整个 payload 转成文本。真实 BuildId 返回值以 `�\0\0\0` 开头,缺失文档的错误文本以 `N\0\0\0Unsupported URL` 开头,破坏版本解析及错误展示。使用 `Unpack::str` 解码版本和错误应答,并添加精确文本与无效载荷断言;现有 `contains(PINNED_BUILD)` 掩盖了该问题。
|
||||
|
||||
crates/ely-lok/src/worker.rs:227 — profile URL 由未经绝对化的路径直接拼接,隔离目录可能被静默忽略。实测以 `../real-profile` 调用 `Worker::spawn` 返回成功,但指定目录始终为空;同一内核使用该目录的绝对路径时创建了 `user/`。前者被编码为 `file://../real-profile`,无法代表调用方创建的本地目录,违反 LOK-01。创建目录后规范化为绝对路径,再按平台正确构造文件 URL;增加相对路径测试,核对实际配置落点。
|
||||
|
||||
crates/ely-lok/src/client.rs:140 — 显式指定的工作进程路径失效时,会静默改用 sibling 或源码目录中的 debug 二进制。实测设置 `ELY_LOK_WORKER=/no/such/ely-lok-worker`,`Worker::spawn` 仍成功运行开发构建,配置错误被隐藏,也可能运行过期协议实现。环境变量已设置时严格校验并返回对应错误;只在变量缺省时执行默认定位,并测试不存在路径和空值。
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:93 — 宏测试无法证明 LOK-11:fixture 中的 `Sneaky` 是空过程,没有自动执行事件绑定;测试只检查文档尺寸和关闭结果。临时将工作进程加载选项改成 `EnableMacrosExecution=true,MacroSecurityLevel=0` 后,`a_macro_document_loads_without_running_anything` 仍通过。使用带真实自动执行绑定、能在隔离目录写入标记的宏样本,断言标记缺失;通过允许执行的对照验证样本确实能触发,并要求放开宏的变异使安全测试失败。
|
||||
|
||||
patches/libreoffice/0001-lok-macos-tile-painting.patch:28 — 新增注释 `Aqua virtual devices cannot paint into a client buffer: paint, then copy.` 共 12 个英文词,违反 agents.md 的十词上限。`tests/scripts/comments.py` 当前未扫描补丁,现有检查仍报告通过。将该新增注释缩至十词以内,并让注释检查覆盖项目维护补丁的新增注释。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:166 — R9 的宏安全回归测试缺陷仍在。样本文档只绑定按钮事件,加载测试未触发该事件;第 170 行的正向对照执行用户配置中的另一份库宏。隔离副本将 worker.rs 的 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,执行 `cargo build -p ely-lok --bin ely-lok-worker --locked` 后,以实际固定 LOK 构建运行 `cargo test -p ely-lok --locked a_macro_never_writes_its_marker -- --nocapture --test-threads=1`,实测仍通过,无法检测 LOK-11 的加载时放开宏回归。修复方向:使用同一份带有效自动执行事件的文档验证禁用和放开两种配置,要求放开宏的变异使安全测试失败;正向执行对照放入独立测试程序。
|
||||
@@ -0,0 +1,15 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:35 — R1 的宏回归测试缺陷仍在。fixture 把 Basic 源码直接放进 `office:script`,并使用无效的 `office:events/office:event-listener` 绑定,内核无法据此执行 `Sneaky`。隔离副本把加载选项改成 `EnableMacrosExecution=true,MacroSecurityLevel=0` 后,`a_macro_never_writes_its_marker` 仍通过。另用含嵌入 Basic 库、模块及 `script:event-listener` 的有效样本作对照,同一补丁内核在放开权限后写出了标记,原实现阻止了标记。替换成经正向对照验证的自动执行样本,并要求放开宏的变异使测试失败。
|
||||
|
||||
crates/ely-lok/src/worker.rs:117 — 关闭清理吞掉目录读取失败及枚举错误,其他删除失败也只记录日志,随后仍以 0 退出。真实补丁内核打开并关闭文档后,将隔离 `profile/tmp` 权限设为 000,再调用 `shutdown()`,实测返回 `Ok(())`;恢复权限后仍有两个 `lu*.tmp` 文件。SHUTDOWN 的成功应答还在清理前发送。将清理失败纳入关闭结果,在清理完成后发送应答,并覆盖目录读取、文件删除失败,保证 LOK-10 的失败对调用方可见。
|
||||
|
||||
crates/ely-lok/src/client.rs:147 — shutdown 在应答缺失或协议错误时直接返回,跳过 `child.wait()`;Drop 再次发送失败后也只写日志。注入完成版本及图块握手、收到 SHUTDOWN 后无应答退出 23 的工作进程,shutdown 已正确返回错误,但释放 Worker 后,存活父进程下的子进程仍被 `ps` 标为 `Z <defunct>`。工作进程崩溃会持续积累僵尸进程。保留原始错误,并让发送、接收和协议失败路径均完成子进程终止及回收;增加释放 Worker 后确认子进程已回收的断言。
|
||||
|
||||
crates/ely-lok/src/client.rs:97 — 新增图块自检直接把文件路径拼进 URL,遗漏百分号编码。真实补丁内核使用名为 `profile#hash` 或 `profile%20literal` 的合法配置目录时,`Worker::spawn` 均返回自检文件的 `Unsupported URL ... type detection failed`;普通目录及相对目录成功。`#` 被解释为片段,`%20` 被解码为空格,导致 LOK-01 的加载流程失败。将自检文件通过正确的本地路径到文件 URL 转换传入内核,覆盖保留字符路径。
|
||||
|
||||
crates/ely-lok/src/worker.rs:300 — R1 的配置路径修复仍未处理 Windows 路径。`canonicalize` 产生的盘符路径及扩展前缀被整体百分号编码;隔离探针把 `\\?\C:\Users\test\profile` 传给实际 helper,得到 `file://%5C%5C%3F%5CC%3A%5CUsers%5Ctest%5Cprofile`,预期本地路径断言失败。该结果没有合法的本地文件 URL 路径,无法指向要求隔离的配置目录。按平台转换盘符、分隔符及 UNC 路径,再编码路径内容;为 Windows 路径增加精确 URL 和实际配置落点验证。
|
||||
|
||||
crates/ely-lok/src/client.rs:71 — R1 的固定版本校验仍只检查全文是否包含提交哈希。注入成功返回图块计数、版本中 `ProductVersion` 为 `99.0` 的工作进程,`Worker::spawn` 返回成功;仅包含该哈希的非 JSON 文本也被接受。完整版本和 BuildId 字段的契约因此没有落实。解析版本结构并逐项核对固定版本及 BuildId,拒绝缺字段、错误字段及畸形载荷,补充这些握手失败测试。
|
||||
|
||||
scripts/build-libreoffice.sh:57 — R2 缩短补丁注释后,脚本无法继续使用已经应用 R1 补丁的源码缓存。将当前固定提交及已打旧补丁的 `init.cxx` 放入隔离缓存,实跑脚本以 1 退出并报 `patch does not apply`:新补丁的反向检查因注释变化失败,随后正向应用又遇到已经修改的代码。现有构建目录可直接复现。为已知旧补丁提供受校验的迁移或缓存重建流程,保留对未知本地改动的保护,并覆盖旧补丁缓存升级及升级后重复运行。
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:169 — R2 的宏回归测试缺陷仍在。正向对照调用的是预先写入用户配置的库宏;文档样本仅绑定按钮事件,加载流程没有触发该事件。隔离副本将 LOAD_OPTIONS 改成 `EnableMacrosExecution=true,MacroSecurityLevel=0` 后,`a_macro_never_writes_its_marker` 仍通过。测试无法检测加载时放开宏的回归。使用同一份带有效自动执行事件的文档分别验证禁用与放开两种加载配置,并要求放开宏的变异使安全测试失败。
|
||||
|
||||
crates/ely-lok/src/worker.rs:249 — 新增 RUN_MACRO 通道在正式构建中直接执行宏,绕过加载选项,违反 LOK-11。实测以 `cargo build -p ely-lok --release --locked` 生成的工作进程运行现有宏测试,`Worker::run_macro("macro:///Standard.Module1.Sneaky")` 成功创建标记文件;该接口与 IPC 分支均无测试配置限制。将允许执行宏的正向对照放入独立测试程序,移除产品工作进程中的宏执行入口,并验证正式二进制拒绝该请求。
|
||||
|
||||
crates/ely-lok/src/ipc.rs:15 — R2 的 Windows 配置路径修复仍生成错误的文件 URL。抽取实际 Windows 分支运行,`\\?\C:\Users\test\profile` 得到 `file://C:/Users/test/profile`,预期 `file:///C:/Users/test/profile` 的断言失败;盘符被放进 authority,指定配置目录无法正确表示。UNC 输入还得到 `file:////server/share/profile`。分别处理本地盘符与 UNC 的 authority/path,再编码路径内容;补充精确 URL 及 Windows 实际配置落点测试。
|
||||
|
||||
crates/ely-lok/src/client.rs:253 — R2 的版本校验仍通过文本搜索提取字段,接受畸形文本及错误层级的字段。注入工作进程返回顶层 `ProductName=OtherOffice`、`ProductVersion=99.0`、`BuildId=wrong`,同时在前置 metadata 对象内放入期望值,`Worker::spawn` 实测成功;去掉 JSON 大括号也被接受。固定构建契约仍可被错误版本响应绕过。解析完整 JSON,严格核对顶层版本结构及字段,并覆盖畸形载荷和嵌套同名字段。
|
||||
|
||||
crates/ely-lok/src/client.rs:182 — R2 的回收修复在关闭协议失败后直接等待子进程,遗漏终止仍存活的工作进程。注入完成版本及图块握手、收到 SHUTDOWN 后回复非法 tag 99 并持续存活的进程,`shutdown()` 超过 4 秒仍卡在等待,只有外部终止才能结束;Drop 走同一路径。保留关闭协议错误,在失败路径终止并回收子进程,再返回原始错误;增加非法应答且子进程继续存活的回归测试。
|
||||
|
||||
scripts/build-libreoffice.sh:58 — R2 的旧补丁缓存兼容改成单行 marker 检查,丢失完整补丁与未知改动校验。隔离缓存保留 `SetOutputSizePixel(...)`,将绘制条件恢复成排除 MACOSX 的旧条件,实际脚本仍跳过补丁并进入 make;使用受控构建工具桩实测退出 0。在真实构建中,该条件会编掉 macOS 图块绘制,产物失去本任务要求的出图能力。校验完整补丁状态,对已知旧注释版本做受校验迁移,遇到缺失 hunk 或未知代码修改时明确失败;覆盖旧缓存重复运行及残缺补丁场景。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:169 — R3 的宏回归测试缺陷仍在。正向对照运行预置用户库中的宏,加载样本只绑定按钮事件,测试没有触发该事件。隔离副本将 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,重新构建工作进程后,`a_macro_never_writes_its_marker` 仍通过,无法检出加载时放开宏的回归。使用同一份含有效自动执行事件的文档验证禁用与放开两种配置,并要求放开宏的变异使安全测试失败。
|
||||
|
||||
crates/ely-lok/src/worker.rs:250 — R3 的产品宏执行入口仍存在,新增保护仅检查运行时环境变量 `ELY_LOK_DEV=1`,公开的 `Worker::spawn_dev` 也会设置它。实测新编译的 release 工作进程运行宏测试时成功写出标记;再用该 release 二进制、设置 `ELY_LOK_DEV=1` 运行 `a_production_worker_refuses_run_macro`,普通 `Worker::spawn` 继承此变量,拒绝断言失败,违反 LOK-11。把宏正向对照移到独立测试程序,移除产品工作进程中的宏执行分支,并验证 release 二进制在该环境变量存在时仍拒绝请求。
|
||||
|
||||
crates/ely-lok/src/client.rs:275 — R3 的版本校验修复仍以字符计数代替 JSON 解析,字符串内的花括号会改变嵌套深度。注入有效 JSON:metadata 内依次放 `"pad": "}"`、三项期望版本字段、`"pad2": "{"`,顶层则放 `ProductName=OtherOffice`、`ProductVersion=99.0`、`BuildId=wrong`;实测 `Worker::spawn` 仍成功。另在正确字段后添加无值的 `"invalid": }`,畸形 JSON 也被接受,固定构建校验继续失效。解析完整 JSON 并严格核对顶层字段,覆盖字符串花括号、嵌套同名字段及畸形载荷。
|
||||
|
||||
scripts/build-libreoffice.sh:57 — R3 的残缺补丁检查缺陷仍在。新增的 `defined(MACOSX)` marker 可由其他条件行满足,无法证明绘制条件已修复。隔离缓存应用当前补丁后,仅把绘制条件恢复为 `#if defined(UNX) && !defined(MACOSX) || defined(_WIN32)`,保留两个 marker;实跑原脚本仍进入受控 make 桩并以 0 退出。真实构建会编掉 macOS 图块绘制,违反 C07.1 的出图要求。校验完整补丁状态,对已知旧注释版本做受校验迁移,缺失 hunk 或未知代码修改时明确失败,并覆盖旧缓存重复运行与残缺补丁场景。
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:169 — R4 的宏回归测试缺陷仍在。样本仅绑定按钮事件,加载测试没有触发该事件;正向对照执行的是用户配置中的另一份库宏。隔离副本把 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,重新构建工作进程后,`a_macro_never_writes_its_marker` 仍通过,无法检测加载时放开宏的回归。使用同一份带有效自动执行事件的文档验证禁用和放开两种配置,要求放开宏的变异使安全测试失败。
|
||||
|
||||
crates/ely-lok/src/worker.rs:250 — R4 的产品宏执行入口仍存在。运行时开关由环境变量换成了 `--dev`,公开的 `Worker::spawn_dev` 会传入该参数,release 构建也包含执行分支。实测新编译的 release 工作进程运行现有宏测试,`run_macro` 成功创建标记文件,违反 LOK-11;环境变量继承的旧路径已修复。将宏正向对照移到独立测试程序,移除产品工作进程中的宏执行入口,并验证 release 二进制在携带 `--dev` 时仍拒绝请求。
|
||||
|
||||
crates/ely-lok/src/client.rs:313 — R4 的完整 JSON 校验要求仍未满足。新解析器接受任意反斜杠转义及由数字、减号、小数点任意组合的值。通过 IPC 注入正确版本字段并追加 `"invalid": --`,或把 ProductName 写成非法 JSON 字符串 `"Libre\Office"`,实测 `Worker::spawn` 均成功;有效的 `"\u004cibreOffice"` 又被误拒绝。R4 的嵌套花括号和缺值载荷已被拒绝,但畸形版本响应仍可通过固定构建校验。使用完整 JSON 解析核对顶层字段,覆盖非法数字、非法转义及合法 Unicode 转义。
|
||||
|
||||
scripts/build-libreoffice.sh:59 — 新增检查阻断全新构建。固定提交的原始 `init.cxx` 本身含有 old_line,脚本在第 64 行应用补丁前就以 `has a reverted tile condition` 退出。以该提交的原始文件搭建隔离缓存,实跑脚本退出码为 1,补丁与构建均未执行。先识别完整原始状态并应用补丁,再校验完整结果;覆盖空缓存首次构建路径。
|
||||
|
||||
scripts/build-libreoffice.sh:63 — R4 的残缺补丁校验缺陷仍在。原先恢复绘制条件的变异现已被拒绝,但两个 marker 仍无法证明全部修改已应用。隔离缓存应用完整补丁后,仅把像素复制分支恢复为 `#ifdef _WIN32`,保留设备分配与绘制条件修改;实跑脚本仍进入受控 make 桩并以 0 退出。该状态会在 macOS 编译掉向 pBuffer 复制像素的代码,使图块保持未写入。校验补丁全部修改及已知旧注释版本,残缺或未知状态明确失败,并覆盖逐项撤销补丁修改的回归测试。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:169 — R5 的宏安全回归测试缺陷仍在。文档样本绑定按钮事件,加载测试没有触发该事件;正向对照执行的是用户配置中的另一份库宏。隔离副本把 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,重新构建工作进程并运行 `a_macro_never_writes_its_marker`,测试仍通过,无法检测加载时放开宏的回归。使用同一份带有效自动执行事件的文档验证禁用和放开两种配置,并要求放开宏的变异使安全测试失败;正向执行对照放入独立测试程序。
|
||||
|
||||
crates/ely-lok/src/client.rs:372 — R5 的完整 JSON 校验缺陷仍在。数字检查接受前导零,字符串解析器在第 319 行直接接收未转义控制字符。通过 IPC 返回正确的三个版本字段,再追加 `"invalid":01`,或追加含实际换行字符的字符串字段,实测 `Worker::spawn` 均成功;追加合法的 `"extra":1e2` 或 `"extra":"\ud83d\ude00"` 又被误拒绝。R5 的 `--`、非法反斜杠转义与 `\u004c` 用例已修复,但畸形响应仍可通过固定构建校验。使用完整 JSON 解析器核对顶层版本字段,并覆盖前导零、控制字符、指数数字和代理对转义。
|
||||
|
||||
scripts/build-libreoffice.sh:58 — R5 的残缺补丁校验缺陷仍在,绘制条件校验也再次缺失。`patched()` 只搜索三个全文件 marker,头文件和像素复制分支共用同一 marker。以固定提交的原始 init.cxx 应用完整补丁后,分别仅恢复旧绘制条件、仅恢复像素复制分支的 `#ifdef _WIN32`、仅恢复 BitmapTools 头文件条件,实跑脚本三次都进入受控 make 桩并以 0 退出。前两种状态会在 macOS 编译掉绘制或像素复制,破坏出图块要求。空缓存首次应用补丁已通过;应校验完整补丁结果,对残缺和未知状态明确报错,并覆盖逐项撤销补丁修改的回归测试。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:169 — R6 的宏安全回归测试缺陷仍在。样本文档只绑定按钮事件,加载测试没有触发该事件;正向对照执行的是用户配置中的另一份库宏。隔离副本将 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,重新构建工作进程后运行 `a_macro_never_writes_its_marker`,实测仍通过,无法检测 LOK-11 的加载时放开宏回归。修复方向:以同一份带有效自动执行事件的文档验证禁用和放开两种配置,要求放开宏的变异使安全测试失败;正向执行对照放入独立测试程序。
|
||||
|
||||
crates/ely-lok/src/client.rs:318 — R6 的完整 JSON 校验缺陷仍在。新增控制字符搜索只匹配 U+0000 和 U+001F,原始换行、制表符和 U+0001 仍直接进入字符串;第 363 行的数字扫描仍排除 `e`、`E` 和 `+`,新增指数检查分支实际不可达。通过 IPC 返回正确的三个版本字段,再追加含上述任一原始控制字符的字符串字段,实测 `Worker::spawn` 均成功;追加合法的 `"extra":1e2` 则被拒绝。前导零已拒绝,合法代理对附加字段已接受,但畸形响应仍能通过固定构建校验。修复方向:使用完整 JSON 解析器校验顶层版本字段,并加入覆盖控制字符、指数、前导零及 Unicode 转义的回归测试。
|
||||
|
||||
scripts/build-libreoffice.sh:64 — R6 的残缺补丁校验缺陷仍在。逆向完整性检查失败后,仍以全文件 marker 将残缺补丁认作旧注释版本。对固定提交的原始 init.cxx 应用完整补丁,分别仅将 BitmapTools 头文件条件、像素复制分支条件恢复为 `#ifdef _WIN32`,实跑脚本均打印 `tile patch already applied (older comment)`、进入受控 make 桩并以 0 退出;像素复制分支会在 macOS 被编译掉,破坏出图块要求。旧绘制条件回退已被拒绝,首次应用完整补丁也通过。修复方向:校验所有补丁修改的完整结果,明确拒绝残缺或未知状态,并加入逐项撤销修改的回归测试。
|
||||
@@ -0,0 +1,7 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:166 — R7 的宏安全回归测试缺陷仍在。样本文档只绑定按钮事件,加载测试没有触发该事件;正向对照执行的是用户配置中的另一份库宏。在隔离副本中将 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,重新构建工作进程并运行 `a_macro_never_writes_its_marker`,实测仍通过,无法检测 LOK-11 的加载时放开宏回归。修复方向:用同一份带有效自动执行事件的文档验证禁用和放开两种配置,要求放开宏的变异使安全测试失败;正向执行对照放入独立测试程序。
|
||||
|
||||
crates/ely-lok/src/client.rs:324 — R7 的完整 JSON 校验缺陷仍在。第 318 行找出原始控制字符后,第 324 行仍把它当作反斜杠处理,按后一个字符解码。通过 IPC 返回正确的三个版本字段,并追加值为 `a`、实际换行(或制表符、U+0001)、`b` 的字符串字段,实测 Worker::spawn 均成功;ProductName 使用实际 U+0001 后接 `u004cibreOffice` 也被解码为 LibreOffice 并通过校验。新增复验还发现字段间 U+00A0 被当成合法 JSON 空白。合法指数与代理对附加字段、前导零拒绝已通过复验,畸形响应仍能通过固定构建校验。修复方向:使用完整 JSON 解析器校验顶层版本字段,并加入覆盖原始控制字符、JSON 空白边界、指数、前导零及 Unicode 转义的回归测试。
|
||||
|
||||
scripts/build-libreoffice.sh:65 — R7 的残缺补丁校验缺陷仍在。完整补丁逆向检查失败后,全文件 marker 仍将残缺补丁认作旧注释版本。对固定提交的原始 init.cxx 应用完整补丁,再分别仅将 BitmapTools 头文件条件、像素复制分支条件恢复为 `#ifdef _WIN32`,隔离环境实跑脚本均打印 `tile patch already applied (older comment)`、进入受控 make 桩并以 0 退出,逆向完整性检查仍失败;像素复制分支会在 macOS 被编译掉,破坏出图块要求。首次应用完整补丁、拒绝旧绘制条件回退已通过复验。修复方向:校验所有补丁修改的完整结果,对残缺或未知状态报错,并加入逐项撤销补丁修改的回归测试。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:166 — R8 的宏安全回归测试缺陷仍在。样本文档只绑定按钮事件,加载测试没有触发该事件;正向对照执行的是用户配置中的另一份库宏。隔离副本将 LOAD_OPTIONS 改为 `EnableMacrosExecution=true,MacroSecurityLevel=0`,执行 `cargo build -p ely-lok --bin ely-lok-worker --locked` 后,以实际固定 LOK 构建运行 `cargo test -p ely-lok --locked a_macro_never_writes_its_marker -- --nocapture --test-threads=1`,实测仍通过。新增“未验证”注释后,测试仍无法检测 LOK-11 的加载时放开宏回归。修复方向:使用同一份带有效自动执行事件的文档验证禁用和放开两种配置,要求放开宏的变异使安全测试失败;正向执行对照放入独立测试程序。
|
||||
|
||||
crates/ely-lok/src/client.rs:319 — R8 的完整 JSON 校验缺陷仍在。控制字符检查只拒绝 NUL,其余原始控制字符仍在第 327 行被当作反斜杠处理。通过 IPC 返回正确的三个版本字段,并追加值为 `a`、实际换行(或制表符、U+0001)、`b` 的字符串字段,实测 Worker::spawn 均成功;ProductName 使用实际 U+0001 后接 `u004cibreOffice` 也被解码为 LibreOffice 并通过校验。U+00A0 空白拒绝、合法指数与代理对附加字段接受、前导零拒绝均已通过复验,畸形响应仍可通过固定构建校验。修复方向:使用完整 JSON 解析器校验顶层版本字段,并加入覆盖全部原始控制字符、JSON 空白边界、指数、前导零及 Unicode 转义的回归测试。
|
||||
@@ -0,0 +1,11 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/lib.rs:10 — [P1] C07.2 要求的 GPUI `LokView` 及视图渲染链缺失。当前仅导出 `Worker`、`Doc`、`Tile`、`Invalidations`;源码没有图块缓存、旧代际响应丢弃、页面裁剪或统一的缩放/滚动/DPI 坐标转换。临时消费程序 `use ely_lok::LokView;` 实测编译报 E0432;同一 960×960 twips 区域在 `worker.rs:269` 始终按 64×64 像素渲染,接口无法请求 200% DPI 所需的 128×128 像素。`docs/engineering-risks.md:29` 所称“客户端按页矩形裁剪”“旧代际图块不覆盖新画面”也缺少实现与验收依据。修复方向:完成可消费的 GPUI 视图、目标像素尺寸与共享坐标转换、按代际接收/丢弃图块,并以延迟旧响应及缩放、滚动、DPI 场景验证后更新证据。
|
||||
|
||||
crates/ely-lok/src/events.rs:122 — [P1] 每次按键或点击均通过 `worker.rs:308、321` 无条件递增代际并设置 `all=true`,覆盖精确失效矩形的作用,违反 LOK-03“只更新必要图块”。实测打开含两段文字的 FODT,读取 512×512 图块与失效批次,再点击 `(150,150)`:点击前后像素完全相同,事件却从 `(generation=0, all=false, rects=[])` 变为 `(1, true, [])`。局部编辑同样只能触发整区重绘;即使后端产生矩形回调,这个无条件全量标志仍然存在。修复方向:恢复可靠的内容区域失效来源,以实际脏区驱动重绘;将纯光标/选择事件与内容变更分开,补充局部编辑仅更新相交图块的测试。
|
||||
|
||||
crates/ely-lok/src/worker.rs:66 — [P1] `WAIT_EVENTS` 同步阻塞唯一的 IPC 读取线程,等待期间后续编辑、关闭和退出请求均无法被读取;非 macOS 的 `serve` 在第 214 行存在同一路径。客户端 `poll_events` 同时占用 `&mut Worker` 并同步等待回复。实测文档空闲、已知代际为 0 时发送文档允许的 `WAIT_EVENTS(id, 0, 0)`,随后发送 `POST_KEY` 和 `SHUTDOWN`,1.5 秒后三个请求均无回复且进程仍存活;当前后端没有自发图块回调,只有被堵住的编辑请求才能推进代际,因而形成永久等待。修复方向:让事件订阅/等待期间继续处理控制请求,提供可完成的取消与关闭路径,并验证空闲订阅后编辑和退出均可完成。
|
||||
|
||||
crates/ely-lok/src/worker.rs:269 — [P1] 图块尺寸直接做有符号除法、`usize` 强转与未检查乘法,错误参数进入分配和 FFI。通过与公开 `Doc::paint_tile` 相同的请求格式实测:`(w=0,h=960)` 和 `(w=14,h=960)` 都返回 `OK`、零像素;`(w=-15,h=960)` 在第 270 行触发 `attempt to multiply with overflow`,worker 中止,客户端收到 EOF,进程内全部会话随之丢失。修复方向:在分配及调用 LOK 前校验文档区域和目标像素尺寸,以 checked arithmetic 核对缓冲区及 IPC 上限,非法请求返回明确错误并保持 worker 可用;覆盖零值、负值、极小值及超大值。
|
||||
|
||||
crates/ely-lok/src/client/tests.rs:210 — [P2] 像素测试把全零缓冲区计为有效深色像素;第 221 行的编辑失效测试只核对代际与标志,未验证更新后的画面。本轮在临时副本中仅将 `Document::paint_rect` 的函数体改为空操作,保留独立的启动绘制自检,重新构建 worker 后,`a_tile_paints_real_pixels` 与 `an_edit_bumps_the_generation_and_invalidates` 仍全部通过;此时实际图块始终为全零。这使当前测试无法守住 C07.2 的基本绘制与更新行为。修复方向:针对包含已知正文的区域验证有效页面/文字像素以及编辑造成的预期画面变化,并要求该空绘制变异触发失败。基线已亲自完成 `cargo build -p ely-lok --bins --locked`、`cargo check -p ely-lok --all-targets --locked`、固定补丁引擎下的全部 10 项测试与注释检查,均通过。
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/worker.rs:286 — [P1] 上轮局部失效缺陷仍存在:编辑键继续调用 `events::bump`,在 `events.rs:127` 无条件设置 `all=true`,精确矩形仍会被整区失效覆盖,违反 LOK-03。固定补丁引擎实测:两段正文开头按 Return,批次为 `(generation=1, all=true, rects=[])`;远处 `(0,7680,3840,3840)` 图块编辑前后逐字节相同,`LokView::apply_batch` 仍清空全部缓存。纯点击及方向键已保持代际稳定,局部编辑的验收仍失败。修复方向:接通可靠的内容脏区来源,以相交矩形驱动重绘,并验证局部编辑保留未受影响的缓存图块。
|
||||
|
||||
crates/ely-lok/src/worker.rs:299 — [P1] 新增 `POST_UNO` 路径执行内容变更后没有产生失效或推进修订;当前固定 macOS 后端也没有图块失效回调。实测先编辑到 generation 1,再依次发送 `.uno:SelectAll`、`.uno:Delete`,重新绘制的像素已经改变,响应仍为 generation 1,事件批次为 `(1,false,[])`。`LokView::watch` 因代际相同直接继续等待,缓存中的旧正文会一直显示,违反 LOK-03。修复方向:将 UNO 内容变更纳入可靠的修订与脏区通知链,补充删除、撤销等命令执行后视图刷新的回归测试。
|
||||
|
||||
crates/ely-lok/src/view.rs:266 — [P1] `Geom::px_of` 已乘 DPI,`prepare` 又把其结果作为 GPUI `Pixels` 布局与绘制尺寸;GPUI 在 `paint_image` 内继续按窗口比例转换到设备像素,造成重复缩放。临时 GPUI 测试实测:zoom=1、DPI=2 时,3840 twips 图块生成 512 像素纹理,却绘制为 512 个逻辑像素;正确逻辑宽度应为 256。此时 `doc_point(256,0)` 返回 `(1920,0)`,正确文档位置应为 `(3840,0)`。因此跨 1x/2x 显示器会改变页面逻辑尺寸、可见范围及滚动距离,违反 LOK-04;现有几何测试仅验证公式往返,未覆盖 GPUI 单位。修复方向:用 zoom 转换文档与 GPUI 逻辑坐标,仅在纹理分辨率转换中应用 DPI,并验证实际绘制尺寸、命中位置及滚动转换。
|
||||
|
||||
crates/ely-lok/src/view.rs:204 — [P1] 图块送入 `RenderImage` 前交换红蓝通道,导致文档颜色错误。锁定版本的 GPUI `RenderImage` 要求 BGRA,`new` 原样保存帧字节;当前 LOK 同样返回 BGRA。固定引擎绘制红色正文实测得到 1182 个红色像素,样本为 `[0,0,255,255]`;将该像素通过实际 `finish_tile` 后,`RenderImage::as_bytes(0)` 变成 `[255,0,0,255]`,GPUI 会显示蓝色。现有黑白图块测试无法捕获此错误。修复方向:按 GPUI 的 BGRA 契约传递图块,添加红蓝可区分的渲染回归断言。
|
||||
|
||||
crates/ely-lok/src/view.rs:155 — [P1] 文档尺寸只在构造时读取,失效处理仅更新代际与缓存;滚动上限、图块请求范围和页面裁剪持续使用旧 `doc_size`。固定引擎实测:初始文档为 `(12808,16408)` twips,输入 80 次 Return 后内核尺寸增至 `(12808,32532)`;调用实际 `apply_batch` 后,视图尺寸仍为 `(12808,16408)`。新页面被第 282 行的范围检查及页面 mask 排除,滚动也无法到达新增内容,违反 LOK-04。修复方向:在文档尺寸变化时同步视图尺寸与滚动边界,覆盖增页、删页后的可见范围和裁剪。
|
||||
|
||||
crates/ely-lok/src/view.rs:139 — [P2] 事件任务被 `detach`,只有代际变化后才检查弱视图句柄;空闲批次始终走 `continue`。释放空闲 `LokView` 后,该任务仍每 50 毫秒轮询并强引用 worker,阻止其释放。临时 GPUI 测试已确认视图弱句柄失效,继续执行 20 个调度步骤后 worker 弱引用仍可升级;显式关闭文档使轮询返回错误后,任务才结束。关闭视图会留下持续工作的事件任务和引擎资源。修复方向:让订阅任务随视图释放而取消,或在每次等待前检查视图存活状态;补充空闲状态关闭视图后的任务与 worker 释放测试。
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/view.rs:168 — [P1] 滚动边界在文档缩短、缩放减小和视口扩大后失效,违反 LOK-04。`apply_batch` 将滚动位置夹到文档宽高,正确上限应扣除当前视口对应的文档尺寸;`set_zoom`(第 95 行)与 `prepare`(第 274 行)改变这一上限后也保留原位置。固定补丁引擎与实际 GPUI 视图方法实测:600 逻辑像素高的视口,80 次 Return 将文档高度从 16408 增至 32532 twips,滚到底部为 23532;SelectAll/Delete 后文档恢复 16408,滚动位置却变为 16408,正确上限为 7408。此时页面裁剪区域的底边落在视口顶部,正文完全消失,直到再次滚动。同样,200% 缩放下滚到底部后改为 25%,滚动位置仍为 11908,正确上限为 0;将视口高度从 300 扩至 1200 逻辑像素也复现 11908 对 0。三项临时回归断言均失败,现有 22 项测试全部通过。修复方向:在文档尺寸、缩放及视口尺寸变化时,按同一坐标转换重新计算水平与垂直滚动上限并夹定当前位置,确保图块位置与页面裁剪使用更新后的值;补充删页、缩小缩放及扩大窗口后的滚动边界回归。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/view/input.rs:166 — 剪切先通过 worker 执行 `.uno:Cut`(worker/input.rs:104),再写系统剪贴板;写入失败时,正文已经被删除。复现:选择 `Hello 世界`,仅向 `clipboard::write` 注入写失败,再执行 `Input::Copy { cut: true }`;返回错误,保存后的段落为 `[""]`。失败时用户内容丢失,重试也失去原选区。修复方向:先取得复制数据并成功写入系统剪贴板,再删除选区;为写失败时正文和选区保留增加回归。
|
||||
|
||||
crates/ely-lok/src/view/input.rs:428 — IME 更新丢弃平台给出的组合内选区,`selected_text_range` 固定返回组合尾部(:397),`text_for_range` 固定返回 `None`(:383)。复现:在 GPUI 空窗口中调用 `EntityInputHandler::replace_and_mark_text_in_range(None, "nihao", Some(2..2), ...)`,再查询选区及 `0..2` 的组合文本,实际得到 `5..5` 和 `None`。输入法移动组合内光标后,平台与编辑器的范围失同步。修复方向:保存并按 UTF-16 更新组合选区、处理替换范围、提供组合文本查询;通过这些平台回调验证组合内编辑。
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:134 — 粘贴只检查 `setClipboard`,然后发送未请求结果通知的 `.uno:Paste`(ffi/document.rs:85)并回复成功;实际导入失败被吞掉。复现:使用已构建的固定引擎,向打开的 FODT 粘贴 MIME 为 `image/png`、数据为 `b"this is not a png"` 的载荷,worker 返回 `OK`;随后保存,文档保持 `Hello 世界`,`<draw:image` 数量为 0。输入队列因此也收不到失败。修复方向:检查支持格式的数据及实际内核粘贴结果,将解析或执行失败回传为错误,并增加合法 MIME 搭配损坏数据的回归。
|
||||
|
||||
crates/ely-lok/examples/lokview.rs:109 — 真窗口鼠标测试仅检查预先设好的焦点(:81),没有断言点击改变光标或拖动改变选区;IME 单测直接调用 `compose`(view/tests/input.rs:47),绕过平台 marked-text 回调,窗口脚本也只输入英文。复现:在隔离副本中移除 `LokView::render` 的全部鼠标处理器,并让 `replace_and_mark_text_in_range` 空执行,启用剪贴板的窗口脚本仍输出 `ok: lokview`,原有 39 项单测全部通过。鼠标桥和中文组合入口失效仍会通过验收。修复方向:从真实窗口事件和平台 IME 回调触发输入,分别断言文档光标、选区、组合更新、提交和取消的结果,并要求上述变异使相应测试失败。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:156 — 粘贴以撤销列表变化判定成功;损坏图片导致的选区删除也会通过该判定,r1 的粘贴失败吞错仍可复现。场景:打开正文为 `Hello 世界` 的 FODT,执行 `.uno:SelectAll`,再粘贴 MIME `image/png`、数据 `b"this is not a png"`。固定引擎返回 `Ok(())`,保存后的段落为 `[""]`,`<draw:image` 数量为 0;用户正文丢失,输入队列收到成功。隔离回归断言失败。修复方向:验证载荷和实际导入结果,保证失败时保留正文及选区,并增加有选区时损坏 PNG 的回归。
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:134 — 剪切的删除阶段统一执行 `.uno:Delete`;Calc 将该命令派发到模态对话框,阻塞 worker。场景:打开 A1 内容为 `Hello 世界` 的 FODS,`worker.copy` 成功取得三种格式,再执行 `worker.delete`;两次独立进程均等待 8 秒仍无回复。进程采样显示 `ScCellShell::ExecuteEdit → SalInstanceDialog::run → Dialog::Execute`。`copy_out` 在系统剪贴板写入成功后进入此路径,单元格剪切及后续输入持续等待。修复方向:按文档类型使用无需对话框的选区删除接口,核验内核结果,并增加 Calc 单元格剪切的窗口回归。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:180 — HTML 仅验证 UTF-8(:161),随后以撤销深度增加判定导入成功;内嵌图片解码失败仍返回成功,并丢失原选区,违反 LOK-06 与失败保留输入要求。场景:固定引擎加载正文为 `Hello 世界` 的 FODT,执行 `.uno:SelectAll`,再粘贴 MIME `text/html`、载荷 `<html><body><img src="data:image/png;base64,dGhpcyBpcyBub3QgYSBwbmc="></body></html>`(图片数据为 `this is not a png`)。worker 回复 `OK`,保存后的段落为 `[""]`、`<draw:image` 数量为 0,原选区消失;将同一 HTML 的图片数据换成有效 PNG,导入后图片数量为 1。隔离回归的失败回传与正文保留断言失败。修复方向:验证 HTML 中实际导入的载荷与内核导入结果,对失败执行完整回滚并恢复选区;补充有选区时损坏 HTML 内嵌图片的回归。
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:178 — 使用 `UndoCount` 的长度差判断成功及计算回滚步数,在撤销历史达到容量后将成功粘贴判成失败,回滚循环执行 0 次,文档已被修改。场景:固定引擎加载 `Hello 世界`,在正文开头连续粘贴 100 次 MIME `text/plain;charset=utf-8`、载荷依次为 `paste1 ` 至 `paste100 `,均成功;第 101 次粘贴 `paste101 ` 返回 `the engine did not paste text/plain;charset=utf-8`。保存正文已包含 `paste101 `,日志为 `failed, 0 steps undone`;两个独立 worker 均复现,隔离回归的有效粘贴成功断言失败。修复方向:按实际操作结果确认粘贴,以能识别历史容量变化的事务边界记录回滚范围;补充撤销历史满时的粘贴成功及失败回滚回归。
|
||||
@@ -0,0 +1,13 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/ffi/document.rs:85 — 命令执行失败被当成成功,违反 LOK-02 和“出错就暴露”。postUnoCommand 关闭完成通知,events.rs 也过滤 UNO_COMMAND_RESULT,worker/input.rs:99 随即返回 OK。实测 Worker::run(&doc, "format.style", &["ElyMissingStyle_034859", "ParagraphStyles"]) 返回 Ok(()),样式仍是 Default Paragraph Style;LokView 的 LokError 订阅者也收到零个错误。在临时副本中仅开启完成通知,同一内核明确报告 .uno:StyleApply 的 success=false。修复方向:接入并关联命令完成结果,将执行失败传至 client 和 LokError,加入失败命令的真实内核回归。
|
||||
|
||||
crates/ely-lok/src/commands.rs:77 — typed arguments 只检查数量,数值内容直接作为字符串转发,允许内核静默转换为兜底值。实测 run("format.size", &["junk"]) 返回 Ok(()),内核随后报告 FontHeight=0、ModifiedStatus=true、Undo=enabled;错误输入已改变编辑状态。修复方向:按映射中的参数类型校验数值及有效范围,client/view 排队前和 worker 执行前拒绝非法参数;回归断言错误可见且文档状态保持原值。
|
||||
|
||||
crates/ely-lok/src/worker.rs:226 — 启用 a11y 后遗漏初始段落快照,首次打开文档时文本桥为空,违反 §7 尾段。载入现有 FODT、聚焦 LokView 并等待布局后,Events.a11y.text 仍为 "";在真窗口中连续四次读取系统 AX 树均得到 textarea value=。同一内核的 getA11yFocusedParagraph 已返回 content="Hello 世界"、position="0"。现有 AX 测试先编辑到 Z,掩盖了此路径。修复方向:启用后读取并发布初始内核文本状态,加入打开已有文档、直接聚焦和读取 AX 的回归。
|
||||
|
||||
crates/ely-lok/src/events.rs:82 — 事件过滤器丢弃 a11y 的单元格和对象文本回调(67–69),使正常导航中的文本桥缺失。载入现有 FODS,方向键从 A1 移至 B1 再返回 A1,内核发送 callback 69:cell="1"、action="create"、name="A1"、text="Hello 世界",poll_events 返回的 a11y.text 仍为空;render.rs:27 将此空值交给辅助技术。修复方向:显式处理这些回调中的 text、paragraph 和移除语义,把当前单元格或对象的内核文本传到视图;增加无需编辑的单元格导航回归。
|
||||
|
||||
crates/ely-lok/src/events/parse.rs:87 — a11y 位置使用无符号解析,内核的 -1 哨兵被忽略,取消选区后公开状态保留旧范围。实测输入 a 后全选得到 A11y.selection=(0,9),右方向键取消选区时内核发送 start="-1"、end="-1";同批次的 selection 矩形已为空,A11y.selection 仍为 (0,9)。该错误位于已导出的内核状态字段。修复方向:按 LOK 的有符号位置及 -1 语义建模,明确清理取消后的范围;加入全选→取消和段落切换回归。
|
||||
|
||||
crates/ely-lok/src/client/tests/commands.rs:27 — 命令状态用 contains(want) 验证,错误字号仍能通过核心映射测试,削弱 LOK-05/12 的交付证据。在临时副本中仅把 worker 收到的 FontHeight 参数 "18" 改成 "118",真实内核报告 FontHeight="118",every_mapped_command_reaches_the_kernel_and_reports_state 仍通过(1 passed);保存结果的现有断言也接受此错误。修复方向:按回调的实际格式精确比较状态值,使 18→118 变异触发测试失败。
|
||||
@@ -0,0 +1,9 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:125 — Check::Enabled 使用异步回调缓存确认本次撤销/重做,随后直接返回成功,违反 LOK-02、LOK-05。实测载入 FODT 并等初始 Undo=disabled 后键入 a,立即 run("edit.undo") 返回 disabled 错误;同一内核的键盘撤销能恢复原文。等 Undo=enabled 后执行一次撤销,立即重做被旧 disabled 状态拒绝,立即再撤销却返回 Ok(()),此时撤销历史已经为空。现有映射测试在每步之间等待状态回调,掩盖此时序。修复方向:按本次操作的当前内核历史确认可用性及执行结果,补充键入后立即撤销、撤销后立即重做和连续撤销回归。
|
||||
|
||||
crates/ely-lok/src/commands.rs:115 — Points 只校验浮点数和范围,放行内核无法原样表示的精度;worker 的后置 Reports 检查报错时已经留下格式修改。实测打开原字号 12 的 FODT、全选后 run("format.size", &["18.01"]) 返回 Err,内核状态变为 FontHeight=18、ModifiedStatus=true、Undo=enabled,保存文件含 fo:font-size="18pt"。修复方向:在 client/view 排队前及 worker 派发前按固定内核的字号表示精度校验参数,错误输入保留原格式;加入精度边界和失败后保存内容的回归。
|
||||
|
||||
crates/ely-lok/src/events/parse.rs:100 — 空段落回调覆盖当前单元格文本,单元格取消编辑后的文本桥丢失,违反 §7 尾段。实测载入 FODS,A1="Hello 世界",方向键先到 B1 再回 A1,F2 进入编辑、键入 Z、Escape 取消;内核发出 kind 62 的 content="",桥接文本随之变空。A1 仍被选中,保存结果仍含 "Hello 世界";真窗口系统 AX 连续三次读到 textarea value=。现有单元格测试只覆盖方向键导航。修复方向:同步单元格编辑退出与取消后的当前焦点文本,处理编辑段落和所选单元格的实际回调时序,加入取消编辑的真实内核及 AX 回归。
|
||||
|
||||
crates/ely-lok/src/view.rs:206 — poll_events 失败只写日志并停止观察,应用的 LokError 订阅者收不到故障,违反“出错就暴露”。实测创建 LokView、订阅 LokError 并等初始 "Hello 世界" 桥接完成,终止其 worker,保持视图存活并推进调度器;订阅者收到零个错误,旧内核状态继续留在视图中。现有失效文档测试随后发送按键,由输入队列暴露错误,覆盖不到空闲观察失败。修复方向:在观察失败路径向存活视图发出 LokError,并加入空闲时 worker 退出的回归。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
crates/ely-lok/src/worker/input.rs:125 — Check::History 用文档级 UndoCount/RedoCount 检查 Calc 单元格编辑中的局部历史,导致命令可用性与执行结果错误,违反 LOK-02、LOK-05。实测载入现有 FODS,A1="Hello 世界",F2 后键入 Z,run("edit.undo") 报 `.uno:UndoCount is 0`,文本保持 "Hello 世界Z";同一 worker 的 Cmd+Z 随即恢复 "Hello 世界"。若先执行 format.bold 留下一条文档历史,再 F2 键入 Z,run("edit.undo") 已把编辑文本恢复,却报 `UndoCount went 1 to 1`;随后 run("edit.redo") 报 `RedoCount is 0`。现有即时撤销回归只覆盖 Writer。修复方向:使用与 UNO 实际作用的编辑上下文一致的同步历史/结果确认,加入 Calc F2 编辑内的撤销、重做以及同时存在文档历史的回归。
|
||||
|
||||
crates/ely-lok/src/commands.rs:123 — 0.05 步长校验的 1e-6 容差放行 `18.05000001`,原始值继续派发;内核以 18.05 执行后,Reports 比较报错,worker/input.rs:155 的 Undo 回滚破坏了用户已有的重做历史。通过公开 Worker API 实测:打开 FODT、键入 a 并撤销,保留可重做的输入;全选后 run("format.size", &["18.05000001"]) 返回 Err;再 run("edit.redo") 返回 Ok,保存文本仍为 "Hello 世界",字号变成约 18.05pt,原本应恢复的 a 已丢失。现有精度测试只拒绝 18.01,覆盖不到步长容差边界和失败前已有 redo 的情形。修复方向:在 client/view 排队前及 worker 派发前严格拒绝内核无法原样表示的字号,加入接近 0.05 格点的输入及已有 redo 时失败请求保持历史的回归。
|
||||
Loaded 100 of 252 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user