Skip to content

ci: 完善回归门槛与构建后 Web 验证 - #48

Merged
massif-01 merged 4 commits into
RMinte-AI:mainfrom
massif-01:massif/ci-gates-plan
Oct 12, 2026
Merged

massif-01 merged 4 commits into
RMinte-AI:mainfrom
massif-01:massif/ci-gates-plan

Conversation

@massif-01

@massif-01 massif-01 commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

现有 CI 没有接入部分已存在的证书、WebSocket、离线终端回归,也没有统一处理文档 PR 的通过条件与真实构建后的 Web 验证。本 PR 按已审查通过的 v2 接入这些检查,新增唯一汇总结果 CI Gate,并让 Release 使用同次完整检查成功的构建工件。

行为与范围

  • 唯一 changes 输出范围和发布资格:白名单文档 PR 可跳过重任务;main push(包括文档)、标签、published、手动构建执行完整检查。
  • Gate 严格要求所选任务成功,拒绝分类失败、缺失/非法输出、取消和意外跳过;取消判断限于最终判定可观察到的状态。
  • 保留 main 按项目版本自动发布;已有 Release 正常跳过并明确记录未发布新资产。新发布时拒绝不匹配标签。保留版本标签和 release: published 上传入口,手动运行仅构建。普通 PR/develop/fork 不发布。
  • 上传前验证本次 www.bin 和 flasher 映射;下载同次真实 web_optimized 后检查 JS 语法、应有 gzip 和内容,再复用实际终端浏览器场景。资源失败进入断言。
  • 证书 current-only 仍准备 restart 输入并执行当前 stop/restart;保留 ASan/UBSan 与已有有效断言。仅适配主机入口,不改固件业务。

验证

本地策略 9 项、发布 3 项、源码 xterm 4 项、真实构建 Web 场景及唯一缺资源负例 3 项通过;三个当前 WebSocket 入口、当前证书完整入口通过。ESP-IDF v5.5.2 实际完整构建、www 映射、13 个成品资源语法/gzip、actionlint 和整个 PR git diff --check 通过。

本地为 macOS/Node 24,Ubuntu/Node 22 以本 PR 最新提交 Actions 为准。没有重放历史坏版本或扩展浏览器/账号/语言矩阵;没有硬件验证、真实 Release 实验或 GitHub 设置修改。

设计及回执:docs/ci/CI改进设计与审查交接-v2.md、docs/ci/RESULT.md。

当前 version.txt 为 0.6.1,上游对应 Release 已存在。本 PR 无发布资格;以后经用户批准按此版本合并 main,完整 CI/Gate 成功后会跳过新资产。尚未发布的合法版本仍按原要求自动发布,不依赖本次修改版本文件或人工打标签。本 PR 不自动合并,不设置必需检查。

上一轮远程验收

提交 1080387ef2f32b5c8c54d187835e182eee4d1c42 的 Actions 运行 已完成:Check scope、WebUI regression tests、Runtime and fan regression tests、Build ESP32-S3 Firmware、Built WebUI smoke test、CI Gate 六项全部成功;Create or Update Release 按 PR 资格规则跳过。

首次 Ubuntu 运行定位到 WebSocket 主机入口缺少 floor 所需的 libm。三个入口补 -lm 后,本地重跑受影响入口通过,最新 Ubuntu 运行完整通过;没有修改业务代码、断言或 sanitizer。

文档 PR 分叉比较修复(2c1ffd2f)

本次提交仅改 tools/ci/check_profile.py 和 tests/ci/test_policy.py。Git 比较加入 --merge-base,保留 base 在前、head 在后,以及 NUL 路径解析、重命名/删除处理和直接错误传播。分类白名单、main 完整检查与自动发布保持不变。

唯一新增回归创建真实 A/B/C 分叉仓库:main 独改 main.c,文档分支独改 README.md。修复前实际返回 README.md/main.c,用例失败;修复后只返回 README.md,真实 classify 返回 run_full=False、publish_release=False。没有 mock Git 差异或预置路径。

本地指定验证:CI 策略 10 项通过、发布 3 项通过、git diff --check 通过。没有额外本地完整构建或浏览器矩阵。最新提交 2c1ffd2 的 Actions 已完成:六项检查(含 CI Gate)全部成功,Release 按 PR 规则跳过。

@massif-01
massif-01 merged commit 9721227 into RMinte-AI:main Oct 12, 2026
7 checks passed
massif-01 added a commit to massif-01/TianshanOS that referenced this pull request Oct 12, 2026
…-prompts

RMinte-AI#47、RMinte-AI#48 在上游是以 squash 方式合并的,分支里保留的是它们原来的提交,直接合并会产生大量假冲突。
这里按正确的共同祖先(RMinte-AI#47 的头提交 bdc987d)做三方合并:我方改动与 RMinte-AI#48 改动没有重叠的文件,结果干净无冲突,
合并后相对 origin/main 的差异就只剩本 PR 自己的改动。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
massif-01 added a commit to massif-01/TianshanOS that referenced this pull request Oct 12, 2026
- version.txt 升到 0.6.2;新增 docs/releases/v0.6.2.md(汇总 RMinte-AI#47、RMinte-AI#48、RMinte-AI#49)和 v0.6.2-validation.md
- 合并到上游 main 后由 Actions 自动创建标签 v0.6.2 并发布 Release(check_release_state.py 的 main 路径)
- README 版本说明、静态资源 ?v= 标识同步为 0.6.2
- tests/ws_subscriptions/test_f1_f2.c:按帧的 type 字段判断“输出帧”,不再被原因代号里的 ssh_output 字样误判

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
massif-01 added a commit that referenced this pull request Oct 12, 2026
* fix: 失败响应保留 data,修复 SSH「测试连接」弹不出主机指纹确认框

SSH 主机页「测试连接」调用 ssh.exec。目标是新主机(1002)或指纹变化(1001)时,
固件把指纹放进 result->data,同时返回非 OK;HTTP 出口的通用失败分支只输出
{code,error} 并释放 data,页面取不到指纹就跳过确认框,用户只看到「连接失败」,
无法在该入口信任新主机,也无法核对并接受指纹变化。

- ts_webui_api.c:通用失败分支同样输出 result.data;没有 data 的失败响应不变。
  同一处顺带加固:code / error 写不进去(内存分配失败)时改发 HTTP 500,
  不再发出缺 code 的残缺 JSON(原有漏洞,由下面的分配失败遍历发现)。
  固件的指纹校验没有改动:确认必须带回当前连接上的指纹,否则继续拒绝,
  校验失败时不发凭据、不执行命令。
- 新增 tests/runtime/test_ssh_fingerprint_wire.py:运行生产的 ssh.exec / ssh.test
  处理函数和 HTTP 响应分支,检查线上字节,并存为契约夹具
  tests/fixtures/ssh-fingerprint;并在构建失败响应的每个分配点注入失败,
  结果只能是完整响应或 HTTP 500。
- 新增 tests/prompts/ssh-fingerprint.browser.test.cjs:把夹具原样回放给真实页面,
  中英文检查确认框内容、取消不信任不重试、确认只发送用户看到的指纹、
  确认时设备换密钥则不信任且不循环。
- test_keystore.py:原先钉着「其他接口的失败响应不带 data」,按新决定改为断言带上
  (只改了这一条断言和模拟字段名,共 2 行);密钥生成原有的失败阶段、清理结果、
  序列化失败返回 500 等测试全部保留,并逐一破坏生产代码确认它们仍会报错。
- docs/repair/ssh-fingerprint/RESULT.md:根因、修复与验证记录。

验证(未接触真机):运行时全套 41 项、静态 122 项、浏览器 249 项、发布检查通过;
ESP-IDF 5.5.2 完整编译通过。还原修复后运行时测试断言失败;夹具换成旧格式后
浏览器测试失败。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat: 指纹变化时的确认框改为明确的安全警告(可选,单独提交)

修复「测试连接」确认框之后,指纹变化(1001)和新主机(1002)弹出的是同一个框:
不说指纹变了,也不显示已登记的旧指纹,用户容易当成普通确认点过去。

- 指纹变化时:标题「安全警告:主机指纹不匹配」,说明可能原因(中间人攻击、服务器重装、
  IP 被分配给了别的服务器),同时显示已登记指纹和当前指纹,确认按钮为「更新主机密钥」,
  默认焦点在「取消」,回车等于取消。
- 新主机的确认框不变。
- 文案沿用已有的 securityPage.* 语言包条目,没有新增翻译。
- index.html 中 app.js 的缓存版本串提升为 0.6.1-lpmu6-keygen2-fp1。
- 浏览器测试补充:标题、按钮文字、两个指纹、默认焦点、回车不接受;去掉本提交后这些断言失败。

验证(未接触真机):静态 122 项、浏览器 249 项、运行时 41 项、发布检查通过。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor: 删除 50 个无入口的旧界面函数及其残留

app.js 删除确认没有任何入口的旧函数 50 个(约 900 行):LED 旧控制与滤镜、
旧 SSH 主机管理、旧自动化变量页、旧动作行等;同时删除它们的 27 条 window
导出、因此失去用途的 selectedFilter / allVariables 变量,以及再也打不开的
「主机指纹不匹配」旧弹窗及其处理函数。app.js 减少约 47 KB(gzip 约 8 KB)。

确认方式:语法树可达性分析加全文搜索(含字符串里的函数名、window.xxx、
模板里的 onclick);删除后重新分析无人调用函数为 0;静态 122 项、浏览器
模拟 232 项与删除前一致。handleModalLogMessage 经 window 在运行时被调用,
不在删除范围内。

input-repair 浏览器测试原先直接调用旧矩阵页的 applySelectedFilter,
改为在现行的弹窗滤镜路径上验证同样的零值保留。macOS 27 对照清单与说明
文档同步。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(firmware): 手动触发被拒和 SSH 连接失败时,如实报告具体原因

样板间 B、C 需要固件多说一点;只加字段、不改任何业务,旧界面照常工作。

快捷操作点击后被拒(automation.rules.trigger):
- 引擎里新增 ts_rule_trigger_ex(),除返回值外还报告拒绝原因:引擎未在接收执行请求、
  规则被禁用、不允许手动执行、配置未加载、需要恢复、配置正在保存、规则正在执行、
  服务状态不允许启动、规则刚被修改。原来这些全被压成同一句 "Failed to trigger rule"。
  ts_rule_trigger() 行为不变(内部调用 _ex)。
- API 把原因代号写进 message(如 engine_not_accepting、execution_busy);
  忙碌类仍用 BUSY 错误码,旧界面还能说"设备正忙"。规则不存在、其他未知原因的返回不变。

SSH 连接失败(ssh.exec / ssh.test):
- 客户端记录连接停在哪一步:域名解析、端口被拒、主机不可达、超时、握手、主机指纹、
  认证(ts_ssh_get_fail_stage);错误文字 "SSH connection failed (code=N)" 与所有返回值不变。
- ssh.exec 的连接失败改用原因代号(ssh_dns_failed、ssh_port_refused、ssh_host_unreachable、
  ssh_connect_timeout、ssh_handshake_failed、ssh_auth_failed、ssh_connect_failed),
  data 里带 stage 和客户端原文 detail;ssh.test 保持原来的成功信封,另加 reason 和 stage。

测试(主机上运行生产代码,未接触真机):
- 引擎:逐一覆盖每种拒绝原因;未知规则不报原因;ts_rule_trigger / _ex 两个入口。
- SSH 客户端:逐一模拟每个失败阶段,并确认原有返回值与错误文字不变。
- test_failure_wire.py:规则保存/启停的回复和触发被拒经生产的 HTTP 响应链,线上字节存为
  tests/fixtures/failure-wire;test_ssh_fingerprint_wire.py 增加每种 SSH 连接失败的线上字节。
  夹具与代码不一致时测试失败,浏览器测试用同一批字节回放。
- 逐一破坏(原因搞错、原因丢失、阶段搞错),这些测试都会报错。
- test_stop_protocol 沿用的是 test_engine 的前半截,桩件原文未动,新开关用宏叠加。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(webui): 失败提示按"动作:原因。下一步。"重写——规则启停保存、快捷操作、SSH 测试连接(样板间)

三个样板间,作为后续铺开到全部页面的范本:
- 规则:启用 / 禁用 / 保存。写出规则名而不是内部 id;原因是人话("它正在执行中"),
  不再先说"提交的信息不符合要求"再跟内部代号;失败了不再写"已启用";成功说"已启用规则“x”",
  而不是 "…: ok";结果未确认单独成句并提示不要重复提交。
- 快捷操作:原来一句"当前不允许启动:请检查启用、手动权限及服务状态"塞了 5 种可能,
  现在卡片知道自己为什么不能启动(规则已禁用、不允许手动执行、引用未解析、详情读取失败、
  服务运行中/启动中/核验中/停止中/未确认已停止),分别说;点击后被固件拒绝的 9 种也各说各的。
  "请等待几秒后再触发其他模型"改为完整句子"刚刚触发过其他快捷操作,请等几秒再试。"
- SSH 测试连接:按连接停在哪一步说(解析不了主机名、对方拒绝连接、无法到达、超时、
  无法建立 SSH 加密连接、主机拒绝了用户登录);取消确认框是中性提示而不是"连接失败";
  确认时对方换了指纹会明确说没有信任它。

公共部分:
- 语言包新增 failure 目录:拼句格式、各操作名称、约 50 个"原因 + 下一步"(中英文)。
- api.js describeFailure():按原因代号取句子;查不到的代号按错误类别诚实地说,
  设备英文原文只放进折叠的"详情";缺参数时降级;绝不出现"说明暂时无法显示"。
- app.js 新的失败提示条:停留 8 秒,鼠标悬停或展开详情时暂停,有"详情"折叠(原因代号、接口、
  设备原文、阶段、时间)和关闭按钮;不会被随后的普通成功提示冲掉。其他页面的提示不受影响。
- runtimeSaveError 唯一的使用者被替换,已删除。

测试:
- failure-catalog:两种语言一一对应;每条文字符合写作规范(不带机器代号、中文里无多余英文、
  原因无句末标点、下一步是完整句子);固件会发出的每个原因代号必须有对应说法(从 C 源码读取)。
- failure-messages:提示条行为;每种语言下的标准句子;规则、快捷操作的真实页面流程,
  回放固件线上字节;SSH 连接失败 7 种 + 老固件无指纹 + 确认时换指纹。
- 受影响的旧测试同步:快捷操作被阻止、SSH 取消、规则保存的 Node 版测试;
  静态检查把 failure 目录整族纳入。

验证(未接触真机):静态 127、浏览器 305、运行时 43 项、发布检查通过;ESP-IDF 5.5.2 完整编译通过。

补充:详情只在有可看的内容时出现(页面自己判断出的拒绝没有接口、设备原文,就没有「详情」链接);
× 与「详情」同一行;新网页配旧固件(只发英文原文)时仍按错误类别诚实地说;
写操作没拿到设备回答一律说"结果未确认"。写法与新增方法见 docs/repair/failure-messages/README.md。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(firmware): 自动化引擎、动作模板、数据源失败时如实报告具体原因

只加字段、不改业务:失败时 message 换成固定的原因代号(网页按语言翻译),
数字错误码按"最近类别"选,旧网页仍能按类别说话。

- 引擎:启动/暂停被拒时按引擎当时状态报 engine_paused / engine_not_running /
  engine_unavailable;停止超时 engine_stop_timeout;重载读配置失败
  config_file_invalid(原始错误名放 data.detail);内存不足 no_memory
- 动作模板:数量满 template_capacity、重复 ID template_exists、LED 滤镜参数
  不合法 led_filter_invalid;服务绑定保护沿用 service_delete_protected
- 动作测试:没跑成功时报 action_failed / action_timeout,动作自己的输出放
  data.detail(折叠详情里看);找不到模板用 NOT_FOUND
- 数据源创建:表满 source_capacity;管理器未就绪 engine_unavailable。
  原先 INVALID_STATE 被说成"该 ID 已存在",其实 ts_source_register 对已有 ID
  是更新而不是拒绝,这句话是错的

结果拼装抽成小函数(fail_with / engine_refusal_token / action_template_write_reply /
action_execute_failure / source_register_reply),主机测试用生产代码 + 生产
HTTP 响应链生成线上字节存进 tests/fixtures/failure-wire/,漂移即失败。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(webui): 自动化页失败提示迁到统一目录(引擎、规则、动作模板、数据源)

按"动作+对象失败:原因。下一步。"重写,成功也说清做了什么、对谁:
- 引擎四个按钮、规则(手动触发/删除/读取)、动作模板(保存/测试/读取/删除)、
  数据源(启用/禁用/删除/创建)及三个列表的读取失败;列表读取失败不再被当成"空列表"
- 保存动作模板的必填项校验改为贴在输入框旁并自动定位(空的是变量名还是值分别提示)
- 规则编辑器里"没有动作模板"和"动作模板列表读不出来"是两回事,分别说
- 页面自己的脚本出错单独说"页面出了问题"并把脚本报错放进详情,不再冒充设备失败;
  写操作的网络中断一律说"结果未确认"(引擎启停等是用 GET 发出的写操作)
- 英文必填提示改成通顺的句子;详情里"设备原文"改称"原始信息"(网络错误文字不是设备说的)

顺带修复:数据源的启用/禁用开关一直先去查"同名规则",查不到就报错,请求从未发给设备
(v0.5.2 起;规则才需要版本检查)。现在直接请求 sources.enable/disable,并有回归测试。

测试:新增 tests/prompts/flows.browser.test.cjs,用真实页面 + 生产固件的线上字节逐场景
回放,中英文句子存进 tests/fixtures/failure-texts/automation.json(评审文案看这份);
pagekit.cjs 抽出公共的页面脚手架;目录测试改为读取每个调用点真正需要的文案,
并检查目录里没有无人使用的条目。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(firmware): 数据源连接测试与配置包导入导出失败时如实报告具体原因

只加字段、不改业务:失败时 message 换成固定的原因代号,数字错误码按"最近类别"。

- 配置包(.tscfg):新增 ts_config_pack_reason(),13 种结果各有自己的代号
  (不是为这台设备生成的 / 签名没通过 / 内容损坏 / 已过期 ……),库里的英文原句放进
  data.detail;automation.{sources,rules,actions}.{export,import} 六个接口改用它。
  导出权限不足 pack_not_allowed、设备证书不可用 device_cert_unavailable;
  写文件失败统一 storage_failed(errno 文字放详情)
- 规则配置包:验证和预览可以,真正导入一直返回 "source_read_only: encrypted rule
  import requires a recoverable writer"——这是功能没做完,不是"只读来源",现在如实报
  rule_import_unavailable(原文放详情)
- 数据源连接测试(REST / WebSocket / Socket.IO 代理):连不上 proxy_connect_failed、
  对方回了 HTTP 错误 proxy_http_error(状态码数字 + 回应开头放 data)、WebSocket
  连接出错/超时/没收到消息 ws_*、Socket.IO 没按协议回应/等不到事件 sio_*;
  原来都是写死的英文句子

主机测试用生产代码 + 生产 HTTP 响应链生成线上字节,存进 tests/fixtures/failure-wire/。
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(webui): 自动化页其余失败提示迁到统一目录(连接测试、配置包、选择列表)

- 数据源「测试」:REST / WebSocket / Socket.IO 的失败说清是哪个地址、为什么、怎么办;
  测试不改任何东西,所以断线说"无法连接到设备"而不是"结果未确认"(write:false)
- 配置包导出 / 验证 / 导入(数据源、规则、动作模板):失败写进对话框结果框,详情放提示;
  对象叫法放进 failure.kinds,英文句中小写;"请先选择文件"说明要选的是 .tscfg 配置包
- 变量 / 图片 / 主机的选择列表读取失败不再被当成"空列表";SD 卡未挂载单独一句
- 设备回的数字(如 HTTP 状态码)可以填进原因文案里的 {status}
- 英文表单提示补冠词、改成通顺的句子

测试:新增 41 个场景,中英文句子存进 tests/fixtures/failure-texts/automation.json。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(firmware): SSH 主机、密钥、指令、执行失败时如实报告具体原因

只加字段、不改业务:失败时 message 换成固定的原因代号,数字错误码按"最近类别",旧网页仍按类别说话。

- 新增公共函数 ts_api_result_reason(result, 类别, 代号, 详情)(原先只在自动化接口里有)和私有头
  ts_api_pack.h(配置包失败的统一回复,自动化与 SSH 两边共用)
- 主机指纹:保存/核对失败 known_host_failed;新主机 ssh_host_key_unknown、指纹变化
  ssh_host_key_changed(数字码 1002/1001 不变)——页面里其他调用 ssh.exec 的地方也能说清
- ssh.exec / ssh.test / ssh.copyid / ssh.revoke:登录信息不全 ssh_config_invalid、会话创建失败
  no_memory、密钥库没有这把密钥 key_not_found、公钥格式错 pubkey_invalid、服务器拒绝部署
  ssh_deploy_failed(服务器的 stderr 放详情)、撤销前检查/撤销失败、命令没执行成功
  ssh_exec_failed;连接失败沿用上一批的阶段代号
- ssh.exec_stream / ssh.cancel:另一条命令在跑 ssh_exec_busy、没能开始 ssh_start_failed、
  没有正在执行的命令 ssh_no_session、取消没送达 ssh_cancel_failed
- 主机 / 指令数量满 host_capacity / command_capacity;密钥重名 key_exists;服务不允许启动
  service_not_admissible
- 配置包导入导出(主机、指令)改用共用回复;写文件失败 storage_failed
- 连接失败的阶段映射挪进 SSH 客户端库(ts_ssh_connect_reason),WebSocket 的命令执行错误也用它:
  ssh_exec_error 消息现在带原因代号 + stage + detail;输出送达不完整 / 结果送达失败改成代号

主机测试按新代号更新(指纹相关的线上字节夹具随之改变)。
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(webui): SSH 主机、密钥、指令页失败提示迁到统一目录

- 安全页:已知主机指纹、主机记录、撤销公钥、删除/导出密钥、部署/撤销密钥的失败都说清
  "对谁做什么失败:原因。下一步。";部署/撤销里缺哪项就把光标放到哪项;主机列表读不出来时
  说明原因,不再只写"加载失败";撤销成功但没能移除本机记录单独说
- 指令页:保存/删除指令、执行(含 WebSocket 推回来的错误)、取消、启动服务、查看日志;
  辅助命令(读日志/查进程)失败不再显示成"无输出";必填项贴在输入框旁;ID 规则一次说全
- 配置包导出(SSH 主机、指令)失败写进对话框;导出句式改成"导出“名称”(种类)失败",
  中文里的英文种类(SSH 主机)前后不再粘连
- 行内失败提示现在也有「详情」链接,和提示条一致("点详情可以看到…"在哪里都成立);
  浏览器拒绝写剪贴板单独说明;列表过期(找不到这台主机/指令)说明是页面过期

测试:新增 53 个场景,中英文句子存进 tests/fixtures/failure-texts/ssh.json 和 ssh-commands.json;
几个旧测试跟着新句子更新。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs: 重写中英文 README,更新 0.6.1 项目说明

* fix: 防止动作模板删除产生失效规则引用

* docs: 更新中英文 README 的 TianshanOS 头图

* fix: implement durable save-only rule pack import

Persist complete versioned rule sets with one selector, exact-byte acceptance records, target-local revisions and restart-pending guards. Validate signing authority, recipient identity and dependencies without publishing imported rules at runtime.

Fix datasource toggle routing and add production crypto, migration, fault-recovery, engine/API and bilingual browser coverage.

* test: 声明删除引用主机测试所需的 POSIX 接口

* feat(firmware): 配置包校验、应用、导出失败时如实报告具体原因代号

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(webui): 全站操作失败提示迁到统一目录(登录与改密、服务与重启、OTA、文件、网络、风扇、LED、证书与密钥、配置包)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(automation): close rule-pack deletion and dependency review gaps

Classify active/saved union states and block new admission after confirmed
pending deletion while preserving existing runtime objects and leases.
Keep ordinary rule loading compatible, retain published source identity,
and scope strict dependencies to active and saved package versions.
Share effective-action/proposed-object validation for direct/reference SSH,
internal-first host fallbacks and unsupported Webhook actions.

Add the directed A1-A3, B1-B3 and C1-C3 production-path regressions and
bilingual pending-delete/error UI checks. Preserve v3 storage, encrypted
trust and save-only/reboot activation. Record red/green evidence and
unverified device/deployment prerequisites in R1-R3-review.md.

* test: link rule-pack math and declare POSIX gate APIs on Linux

* feat(webui): 合并后补齐上游新功能的提示语(规则配置包导入、规则待重启/待删除、模板缺失)走统一目录

- 规则配置包验证/导入失败、结果未确认改由 failure 目录出句子(原因 22 条新增;旧的 rulePack.errors 整张表删除)
- 快捷操作:待删除/未生效的规则被拒时说明原因
- 固件:config pack 新结果码(时间未确认、未配置信任、签名者无权限、凭证变化)映射为原因代号;引擎拒绝待删除规则时给出 rule_pending_delete
- describeFailure 读取错误自带的 data(上游让 ApiOperationError 带上 data)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(webui): 验证中发现并修复的问题

- 44 个自动转换留下的“保存失败/设置失败”式提示改成“动作+对象”,并补上“结果未确认”的说法
- 设备原因不再被页面自己抛的 Error 吞掉:OTA 上传/服务器、证书、配置包都带原因代号(pageFailure)
- 登录失败只在表单里显示一次;目录读取失败不再套用“上传结果不受影响”;更新检查不再重复“检查更新失败”
- 密钥生成弹窗保留失败阶段与清理结果;规则保存缺少动作模板时说出模板名
- 折叠详情里的“接口”统一写成 storage.mkdir 这种形式;错误自带 data 时也显示阶段与原始信息
- 删除 198 条已无人使用的语言包旧键和 1 条已不存在的原因(规则导入已实现)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test: 跟上合并与提示语迁移——新增页面场景测试、调用点参数检查,修正钉着旧句子的断言

- failure-catalog:新增“每个调用点都给足句子里的占位值”检查;一次列出全部问题
- flows:新增 system 组(登录、重启、服务、文件、网络、OTA、证书、配置包)和阻断删除弹窗、规则包导入场景;黄金句子重生成
- 上游测试适配:rule-pack/delete-reference 用 describeFailure 取句子;runtime 测试补桩与抽取清单
- ws 适配测试:补 ts_ssh_connect_reason 桩;帧类型按 type 字段判断,不再被原因代号里的 ssh_output 误判

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test: 补上“待删除规则被拒绝”的引擎、线上字节与页面用例;去掉一处行尾空格

- 引擎:删除待重启的规则,手动触发给出 TS_RULE_REJECT_PENDING_DELETE(优先于“正在保存/正在执行”),自动评估保持静默
- 线上字节:新增 wire-trigger-rule-pending-delete.json,并让“每个拒绝原因都有代号”的循环覆盖到它
- 页面:触发该规则的句子写入黄金清单;快捷操作卡片在“待删除/还没生效”时各自给出原因

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(webui): 文件选择器读取目录失败时保留设备给出的原因

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* 发布 v0.6.2:版本号、中文发布说明与验证记录

- version.txt 升到 0.6.2;新增 docs/releases/v0.6.2.md(汇总 #47、#48、#49)和 v0.6.2-validation.md
- 合并到上游 main 后由 Actions 自动创建标签 v0.6.2 并发布 Release(check_release_state.py 的 main 路径)
- README 版本说明、静态资源 ?v= 标识同步为 0.6.2
- tests/ws_subscriptions/test_f1_f2.c:按帧的 type 字段判断“输出帧”,不再被原因代号里的 ssh_output 字样误判

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test: 证书页测试跟上新的失败提示方式

CI 的证书回归按名字从页面脚本里取 certError 并断言设备文字原样显示;迁移后该函数已删除,失败提示改由
failure 目录生成。测试改为:直接运行 api.js 里生产的 describeFailure 与 app.js 的 packBox/failureHtml,
断言句子来自目录、设备给的类 HTML 文字只以转义形式出现在折叠详情里、断线按“结果未确认”处理且只提交一次、按钮恢复。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: 同步 sdkconfig 新增配置项的默认值;新增 build/ 清单生成脚本

- #47 新增的配置包签发根(CONFIG_TS_CONFIG_PACK_SIGNING_ROOT_SHA256,默认空)还没写进已跟踪的 sdkconfig,
  每次构建都会把它补上;这里先提交,保证从提交出来的干净检出构建不会改动已跟踪文件
- tools/make_firmware_manifest.py:发版时把干净构建的 6 个镜像/映射文件复制到 build/ 并生成 firmware-manifest.json,
  并检查构建版本与 version.txt、被构建提交一致(HASH 后带 d 的未提交修改构建会被拒绝);--verify 核对 build/ 与清单

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* 发布 v0.6.2:更新 build/ 默认刷机镜像到 0.6.2

- 从干净检出的提交 774c3a9 用 ESP-IDF 5.5.2 构建;版本 0.6.2+774c3a97.10130158,主固件 2,226,000 字节
- build/firmware-manifest.json 记录版本、被构建提交、源码输入哈希和 6 个文件的 SHA-256(tools/make_firmware_manifest.py 生成,--verify 通过)
- 验证记录补充:镜像来源与更新方法、证书回归已在本地跑通

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant