# changelog-archive.md — 更新记录表历史归档 > 全部版本历史都堆在这里(AGENTS.md 不再设更新记录表)。**新条目倒序加在表格最上方**。细节写进对应详档,这里只留一行摘要。 | 时间 | 版本 | 更新内容 | 更新者 | | 2026-10-11 | **0.14.6(本地开发候选,未发布;versionCode 50)** | 九项用户可见修复:更新检测误报「没有 arm64 版本」(资产命名历史翻转);非内置 Provider 模型缺推理档位(保存时补齐,字段级/幂等/不覆盖);启动页一键分享诊断包(系统分享面板,严格脱敏,未点击不发送);插件装载失败(`got` 传递依赖闭包缺失,根因 + 递归闭包门禁收录);「安全模式启动」看起来生效但界面继续报错(未先停旧引擎);部分真实网关的模型「补齐」静默失败(模态过滤);「检查更新」实际只更新运行时快照且健康态根本点不到(新增健康态入口 + APK 更新优先,快照链下线);更新失败卡住启动页(更新不占用主按钮 + 超时收敛);下载失败后「重试」永远点不动(`canStart` 由 state 单一真源派生)。**双 ABI 本地候选**:x86_64 `154202844 B` / `BA5C7D4B5AF0F67B8ABD5B5514D1D6DF33BCED6CDDFCF23AFAE212254070A18D`;arm64 `158765876 B` / `B9AEF12DAD6CCAC03881A09257574C53E4EC622DE7356ADC02CD51A60797DBD7`。**未发布**(无远端写入,未 push / 未开 PR / 未发 Release)。细节见协调仓 `release/v0.14.6/notes.md`。 | dev-core | | 2026-10-09 | **0.14.5-fx-2(本地开发候选,未发布)** | 公共特权调用拒绝伪造 internal;动画设置窄接口;来源构建隔离;服务慢速属主复查;通知延后耐久日志;来源组件绑定 APK commit;维护文档生成与提示检查;移除 Release 无效 skip 输入。vc49。开发自检通过,完整双 ABI、设备与覆盖升级验收仍待第三阶段。见 `docs/HANDOFF-0.14.5-FX-2-DEVELOPMENT-2026-10-09.md`。 | Codex | | 2026-10-09 | **0.14.5-fx-1(发版候选,待 GitHub Release workflow 完成)** | #341 为可见且挂载的浏览器舞台续租原生 bounds,生命周期恢复时重绘并重绑替换的 stage;不 reload 隔离页。#342 将确认的端口冲突及分类运行时故障带到可操作错误页,未知故障不误报权限问题。#340 宽屏布局使用动态 WindowInsets 顶部值,沉浸切换不留固定空白。版本 vc47→48。#108 由用户标记 not planned,本轮未实现完整备份/恢复。设备验证的精确 PASS/INCONCLUSIVE 与未测矩阵见协调仓 `docs/maintenance/0.14.5-fx-1/06-validation-report.md`;本条不是发布完成声明。 | Codex | |---|---|---|---| | 2026-10-05 | **(未发版)客户端插件装配失败纳入引擎故障自愈链(页面侧 + 壳侧 + 回滚侧)** | 页面出现 "Failed to load plugins" 从「壳侧只落一行 `source=console-error` 诊断」升级为终局契约:页面侧新增 `[dsh-boot-failed]` 契约行(前缀 → `dsh-boot-diag` → `source=page-plugin-fail` → `detail=` → `failedIds=` → `pageSideRuntime=`,字段序即契约;失败判据 = 启动页仍在场且有失败投影,幂等),并把 `publishReady()` 判据收紧为 `rendered() && !bootPagePresent()`;壳侧 `LogCollector` 加前缀常量/前缀识别/`clientFailedIdsOf` 纯解析与 `routePageConsole` 第三分支,`EngineStartFlow.onClientPluginTreeFailed` 落 `boot-fail` 终态 + 独立 latch + 一次性回滚编排(前台切错误相位,非前台挂起待 `onResume`),`MainActivity` 的 `showWeb`/`engineMonitorRunnable`/`freezeRunnable` 三处让路防弹回坏页面;回滚侧 `UndoGate.onClientPluginTreeFailure` 一次性入口(刻意不用 ARM→WAIT 观察窗)+ `PluginMounts.clientPullCandidate/pullByClientIds` 唯一点名外科拔除,点不出名且清单未变才 known-good 整份,两者都不成立则不动作并如实停在错误页。**一并修掉两条既有缺陷**:模板字面量未知转义吞反斜杠(`failedIds` 恒 "-")与旧 `publishReady()` 把失败页当已就绪(块L stall 诊断永不触发 + `pageReady()` 每拍清零重试预算)。**测试数字(实测冻结值)**:全量 JVM **1063 tests / 0 failures / 2 skipped / 101 类**,新增 `ClientPluginFailRouteTest` **26** 例、`ClientRollbackGateTest` **24** 例;基线 `scripts/kotlin-test-baseline.json` = 1063(apk 仓与协调仓镜像一致)。**设备验收(MuMu 16416 竖屏)**:注入客户端坏插件 → 页面契约行 → 壳侧 `boot-fail` 落盘终态 → 引导页错误相位 → 自动拔除(`pulled plugin=@dsh-android/dsh-client-bad-probe`)→ 强制重启引擎(PID 更换)→ 自动重载恢复,**45s 内自愈、全程未手动冷启动**;`scripts/verify-client-plugin-fail-recovery.mjs --self-test` 已跑绿(55 例)。**设备层抓出的四段真实缺陷链(代码层与 CDP 层均看不见、仅设备层可抓——这正是三层验收的价值)**:① 匹配面错——`failedIds` 是 loader entry 的 **name**(=包名),不是清单 `- id:`,按 id 匹配恒 0 命中、拔不掉;② 只 `showWeb` 不 `reload`——数据修好了但屏幕不更新;③ `startEngine` 不带 `force`——引擎不重启、继续服务旧 manifest(reload 也拿回同一份坏清单);④ `reload` 早于引擎 listen——ERR_CONNECTION_REFUSED,页面显示「页面加载失败」。**本轮未做(如实登记)**:设备侧 CDP 层未跑;横屏 16384 未跑;arm64 真机未跑;双 ABI 由发布链产出。 | 本会话 docs(T5) | | 2026-10-03 | **(未发版)issue #319:入口清算探测与 `state()` 拆锁(壳侧 Kotlin)** | `RootOwnershipJobs` 拆成状态锁 `lock`(只守字段、禁探测)与入口串行锁 `entryLock`(串行化「清算探测 → 复核 → 置 running」);最长 15s 的真绑定探测不再挡 `state()` 与 root 状态桥调用。原子性不变(`running` 仍落在状态锁内置位;清除原子性仍由租约锁内 guard 保证)。方案与用例来自 PR #322(@Ni-ShuWu)。 | 本会话 Lead | | 2026-10-01 | **0.14.3(未发版):issue #309 闸门 A/B 互锁修复(壳侧 Kotlin)** | **缺陷**:闸门 A(`EngineManager.startEngine` 的 `liveRuntimeComplete()`,位置先于 `force`/可用性判定 ⇒ 看门狗 force 也绕不过)拒启 ⇒ 不 spawn ⇒ `engine.log` 永不产生 ⇒ 闸门 B(`EngineStartFlow.maybeSelfHealDamagedRuntimeTree` 读当拍 `engine.log` 尾部)判据恒为假 ⇒ 自愈结构性不可达;`refreshSnapshot` 调用点全仓仅冷启动一处,拒启路径为零;UI「重试」只 `clearRefreshLedger()` 不删指纹,指纹新鲜时是 no-op。issue 实测 36 次 / 47 分钟零恢复动作。**修法**:拒启路径新增「先取证 → 写 `.runtime-tree-damaged` → 删 `.snapshot-fingerprint` → 清刷新账本 → 落诊断镜像 → 如实写 boot-fail」,借既有冷启动分支 `if (!snapshotFresh()) refreshSnapshot(...)` 走完整重抽取,**不新增 refreshSnapshot 调用点**。**两道闸门**(判定集中在 `RuntimeTree.allowStartRecovery`):① 预算复用 `runtimeTreeHealedThisRun`(每次 app 运行一次);② 证据分级 `START_RECOVERY_CONFIRMED_ENTRIES`(只含快照自身条目 `bin/node` / `dsh/lib/bin.js` / `profiles/web`)——`REQUIRED_LIBS` 成员只记录不自动触发(该表不含传递依赖,命中可能是假阴性;issue 原文告诫「不要贸然补全该表」,自动放宽等于每次启动白付一次 8-12 分钟全量抽取并抹掉现场)。**用户显式出口**:`GuidePageRenderer.enterSafeMode`(错误页主按钮)在 `lastStartRefusalCode == REFUSAL_LIVE_RUNTIME_INCOMPLETE` 时以 `userForced = true` 强制重做一次——放行分级、**不放行预算**;理由是 issue 现场用户只能靠壳侧终端手工补库,等于没有出路。**新增 API**:`EngineManager.invalidateSnapshotFreshness(): Boolean`(删指纹,返回值决定落盘文案——删不掉时写 `live-runtime-incomplete-recovery-blocked` 而非承诺重抽取)、`lastStartRefusal{Code,Confirmed,Evidence}`、诊断包三字段 `start_refusal_{code,confirmed,evidence}`、`RuntimeTree.{START_RECOVERY_CONFIRMED_ENTRIES,confirmedDamage,allowStartRecovery,describeEntry}`。**不改闸门 A 判据本身**:带病的树仍不被 spawn;与 #240 降级闸门不冲突。**测试**:新增 `Issue309StartRecoveryTest` 13 例(判据靠传参 + 源码接线断言:合同必有调用点、动作顺序、文案如实性、取证先于删指纹),全量 Kotlin **959** 项通过,`check-kotlin-test-count` 基线由 654 升到 959(0.14.3 期间积累的多类新增),`check-patch-mirror --require-peer` SKIP=0(基线双仓镜像同步),`check-code-map` PASS;注入两处变异后实测 2 failed 并已还原。设备层(真机/模拟器删件冷启动、低置信度项、预算三档)**未做**,见 `docs/0.14.3-TESTER-CHECKLIST.md` I309。 | 本会话 Lead | | 2026-10-01 | **0.14.3(未发版):#308 合并 + 追上游 0.2.0-rc.2 + 双 ABI 快照重建(未获发布口令)** | **#308 已合入**(head `d67499a` / merge `9006380` / main `5537b57`):AI root 授权面与属主自愈的维护者修订版,两项 required CI 在该 head 上 SUCCESS。**追版 0.2.0-rc.2**:`baseline` 与 `docs/UPSTREAM-CONTRACT.md` 同批抬到 `0.2.0-rc.2`(§9 文档同源判据);`clientSlots` 补 `sidebar.right.pane.tab.title`(keyed/session)与 `sidebar.right.tab.menu.item`(list/session)两条本轮新消费座位。**本轮修掉的会崩缺口**:上游 `otel` 是 enabled 基础服务,其 `lib/index.js` 静态 `import 'got'`,旧快照无 `got` ⇒ boot 期 `ERR_MODULE_NOT_FOUND`;登记 `got@14.6.6` 后重建双 ABI 快照(arm64 162,854,368 B `d81ee2ac…`;x86_64 160,382,696 B `209fc4df…`),两 ABI `check-engine-overlay` 412 条断言全绿。**门禁实修**:`check-engine-overlay` 包根推导 `lastIndexOf('/lib/')` 在 `usr/lib/...` 上恒命中索引 3(退化成 `usr/package.json` 假红),改取 `/lib/` 与 `/dist/` 较后者;发现式目标此前按 dict 取值而实传 list(扫描整体崩),改独立 discovery 表。**工厂件结构变更**:`profile-web.cordis.patch.yml` 因上游新增 `desktop-product-telemetry` / `product-analytics` 两行(Android 面显式 disable)由 20 条顶层项变为 22 条,`FactoryProfilePatchTest` 两处硬编码期望值(`topLevelBlocks` = 22 + 1 注释头 = 23)同步更正并写明依据;Kotlin 全量 **946 项通过**。**组件测试环境**:`dsh-client-ui-responsive` 此前从未导入带 CSS 的引擎包,新 spec 暴露 vitest 外置 `node_modules` + 编译产物裸 `.module.css` 边界,补 `clsx`/`zustand`/`immer`/`simple-icons`(版本取上游 lock 解析值)与 `vitest.config.ts` 的 `server.deps.inline`,**33 文件 / 446 项全绿**;并修掉新 spec 内一处 ASI 缺陷(`vi.mock(...)` 后紧跟 `(` 开头的行)。**issue #309 已复核属实但本轮未改**(live 树缺件时闸门 A 拒启与闸门 B 自愈互锁),登记进 `known-gaps.md`。**边界**:三层设备验收由外部执行、未验收;发布未授权。 | 本会话 Lead | | 2026-09-30 | **(未发版,PR #302)issue #262 方案 A:AI root 权限授权开关 + 应用级 root 授权面与 root 属主自愈** | **壳侧**:`RootGrant.kt`(开关位与「已阅读」同意持久化,同意绑 versionCode、取消勾选连带关开关;决策纯函数三道门 = 通道身份 / 同意 / 应用级 root);`RootAccess.kt`(**su 直连**:显式请求触发 Root 管理器授权框、状态检测、管理器识别与拉起、`execRoot` 执行原语、su 版属主修复——不依赖 Shizuku);`LocalDocs.kt` + `assets/docs/root-disclaimer.html`(免责声明走 APK 内文档通道,离线/随版本/不可远端替换);`ShizukuTransport.rootGateRefusal`(通道 uid==0 且未授权 ⇒ 特权执行面整体 fail-closed,不做假隔离)+ **AIDL v3**(`configure`/`repairOwnership` + `writeChunk` 写后属主自愈,`lchown` 不跟随符号链接)+ `ShellOps` 通道级失败时回退 su;`EngineStartFlow` **启动前置自愈** + `GuidePageRenderer` **失败相位自动自愈**(自愈必须在启动路径上:root 属主会让引擎/快照事务读不了自己的文件 ⇒ 启动即挂,而启动挂了的用户进不到设置页)。**桥面**:`rootGrantState/setRootGranted/setRootConsent/openRootDisclaimer` + `rootAccessState/requestRootAccess/openRootManager/repairRootOwnership/requestShizukuPermission`。**UI**:手机控制页新增开关 + 「已阅读」复选(蓝色超链接开免责声明)+ Root 授权行(检测 root 授权 / 修复文件属主);**不做「打开 Root 管理器」入口**(2026-09-30 主人指正:各家管理器包名/入口不一、部分 ROM 没有管理器 App,打开不保证成功;能刷 root 的用户自己会开 ⇒ 只做诚实引导);非 root 通道置灰 + **红字「无法在未 root 的设备上赋予该权限」**(逐字);uid 2000(Shizuku 以 ADB 启动)分流引导「在 Shizuku 内以 root 启动」。**测试**:`RootGrantTest`/`LocalDocsTest`/`RootOwnershipTest` + phone-control.spec 新增 18 例;门禁 `--run` 30/30 PASSED(含 `check-bounded-io` 抓出并修掉的裸 `readText()`)。**三层验收**:真机 arm64(root 授权 granted uid=0、属主自愈 `healed=2/failures=0`、启动前置自愈实测)+ MuMu x86_64(`verify-webview-015.mjs` ALL PASS 39;非 root 设备红字逐字在屏、免责弹窗不崩)。**本 PR 未动版本号**(由维护者在发布时决定)。 | 本会话 Lead | | 2026-09-29 | **0.14.2-fx-3(vc44):#295 启动归属与认证归因收口;#288 安全停步** | **壳侧**:端口所有权不再由 HTTP 状态单独推断;受管子进程、当前世代 active `engine.log` 令牌、精确 origin 与主框架 401 共同约束自动恢复;未知世代、旧代/轮转日志、TOCTOU 端口竞争 fail closed,外部监听器不盲杀,403/子资源/非精确 origin 不触发全局恢复。移除误导性的手动「重新认证」入口。新增 EngineAuth/EngineProbe 反证测试,Kotlin JVM 全量 **806** 项通过。**#288**:未暴露实时 `[data-undo-panel]`/`[data-undo-diff]` 证据,按安全停步不改 CSS、不伪造证据。三仓基线与镜像门禁已复核;双 ABI 构建与 MuMu 双方向验收进行中。 | 本会话 Lead | | 2026-09-26 | **0.14.2 P1+P2:Shizuku「重置链接」+ 模型能力来源梯(S3 models.dev / S5 用户兜底)** | **P1 壳侧**:设置页「手机控制」新增「重置链接」(与「刷新状态」同行,复用既有 `settleLinkCall`,**不新增定时器**)。承重墙 = `ShizukuTransport.resetConnection` 调 `Shizuku.unbindUserService(args, connection, remove = true)`(AAR 实测 = `IShizukuService.removeUserService(conn, forRemove = true)`)**强制移除 Shizuku 侧僵尸 UserService**——现场「重新授权与重启 App 均无效」证明坏态在进程外,只清本侧标志位永远无效;配套清 `service/connectedAt`、僵尸 `bindLatch` countDown 并置 null、`ShizukuBindState.onReset()`(新增结构化 code `ShizukuBindCodes.RESET`)、`ControlCarrier.invalidateShizukuCache()` 令 caps 5s TTL 立即失效。非阻塞、写后回读 status JSON、**不承诺已修好**。新桥方法 `resetShizukuConnection()`(`@JavascriptInterface`)。单测 Kotlin **669** / UI **307**。**P2 引擎侧**(`plugins/dsh-model-capability`):新增 `src/models-dev.ts`(339 行)+ `test/models-dev.test.mjs` + fixture,来源梯扩为六级 S1 endpoint→S2 vendor→**S3 models.dev(新)**→S4 engine-catalog→**S5 用户显式 fallbacks(新,出厂无默认值)**→S6 active-probe。**合流不是替换**;未知保持未知(**`reasoning===true` 推不出档位集**,拿不到 `effort` 档位时保持未知、不回退)。网络**只在 `ensureModelsDev` 一处**,tick 只读 `$DSH_HOME/models-dev.json` 缓存(**0 次网络**,E-P2-4 单测带反证)。实测 models.dev **223 provider / 8181 模型 / 压缩后约 1.3 MiB**。插件单测 **82/82**。**镜像**:35/35 + 195/195 一致,门禁 **13 PASS**。**门禁**:`check-code-map.mjs` PASSED(新增 `plugins/dsh-model-capability/src/models-dev.ts` 进 P04 覆盖账本,否则 `[uncovered]` 判红)。**坑位 178-180 登记**(重启无效⇒查进程外;出厂默认值=凭空造事实;快照指纹相同≠插件内容相同)。**文档**:modules.md(4.1 插件文件表 + 来源梯六级表)、BRIDGE-API.md(0.14.2 增量节)、本表。 | 本会话 Lead | | 2026-09-25 | **0.14.2(vc41,覆盖安装 v0.14.1 vc40):无障碍归一化基准修复 + 插件摘除条目化 + 追上游 0.1.7-rc.1 三件套** | **壳侧**:`CoordBasisPolicy.kt` 新增(141 行,issue #258)——分屏/自由窗口/悬浮窗下 `nx`/`ny` 归一化基准取了**当前窗口**尺寸而非整屏(实报 HUAWEI BTK-W00 2200x1440、窗口 733x1389,`nx=0.712` 落到 522 而非 1566 ⇒ DSH 自身浮窗整块点不到、连最小化按钮都点不到);优先级 `maximumWindowMetrics`(API 30+)> `getRealMetrics`(26-29 唯一整屏途径)> 当前窗口兜底并回显 `window-current` 标记;`resolve(Reading)` 为纯函数、取数可注入;四路(click/longClick/快照 screen 字段/滚动兜底手势)统一经 `DeviceControlService.coordBasis()`,返回值并带回 `basis`/`basisWidth`/`basisHeight`;反证用例 `CoordBasisPolicyTest` 9 例(含真实缺陷数值 + 「不得等于窗口尺寸」双向断言)。`SnapshotTransaction` 修 apk #249:摘除插件的单位从写死的「id 行 + 紧邻 name 行」改为**整个缩进条目**,条目是 `- insert:` 唯一子项时连包装行一并弹出(旧实现摘成空壳键 ⇒ 设备 boot 期 TypeError);判据 `removedProfilePluginLeavesNoDanglingInsertWrapper`。 **追上游 0.1.7-rc.1**:三条运行时资产按 rc.1 快照重建(attachment-local 47,321→47,937 / session-persistence-jsonl 137,514→145,247 / fs-local 41,407→43,405,三条全部不同源);引擎树补丁新增 `narb-android-N1`(Android 无 `node-addon-require-builtin` 预编译产物 ⇒ 回落 `require(moduleId)`,壳侧 `--expose-internals` 恰暴露所需 `internal/modules/*`;设备 `boot-fail.log` 连记 4 轮 host preparation failed)、重锚 A4/G3/N2 三条(探针 `probe-engine-anchors.mjs` 14/14 OK)、撤销 5 条(N1 旧 patchReload / G1 / A5 / A3 / C3,各带收益归零证据);引擎 overlay `vendorTop` 17→36、`cosmokit` 1.8.3→1.8.5、补登记 `@xterm/headless` 6.0.0 与 `@xterm/addon-serialize` 0.14.0,许可清单 41→59 条;profile 显式禁用上游 `office-to-pdf` 行(`libreoffice-kit` 在 Android 无条件抛 `Unsupported LibreOfficeKit host: android-x64`,不禁则 boot 崩;凭据成对登记进 `check-engine-overlay.mjs` 的 `RUNTIME_MISSING_OK`)。版本号 vc 40→41 / versionName 0.14.1→0.14.2。**文档**:`modules.md`(CoordBasisPolicy 行 + SnapshotTransaction #249)、`RUNTIME-PATCHES.md`(§7.4 追版全段 + 资产字节/注册行号实测刷新),本表。 docs-0142 | | 2026-09-22 | **修复:文件写工具在 Android 上建不了新文件(坑 171 / apk issue #246)** | `dsh-fs-local` 的 `createIfAbsent` 发布站点是全包唯一的 `link(2)` 调用且失败即抛、无任何回退;Android 应用域恒拒 hardlink(EACCES)⇒ 真机上 `write` 工具**建不了任何新文件**,而覆盖已存在文件正常(走 `rename`)——症状为「只能改不能建」。定位到退役记录的空洞:§「已退役资产」称 link(2) 回退族「改由构建期补丁承担」,但 §7.1 表里没有 `dsh-fs-local` 行、快照内也 grep 不到既有回退标记(0 次);EngineManager 注释「fs-local rename fallback is upstream-native」对 0.1.5-rc.1 不成立(上游把 rename 用在替换路径,新增的独占创建路径又裸用 link)。修法:`EACCES/EPERM/ENOTSUP` 时改用 **O_EXCL 占位 + rename** 等价实现 no-replace(保住独占语义:输家仍得 `EEXIST` 与同一拒绝文案),`rename` 失败回收占位防 0 字节残留;**双路同源**——构建期补丁 `fs-local-link-F8`(registry + IMPLS)+ 运行时资产 `fs-local-index.js`(41,407 B,`:767` 注册)。行为回归 `scripts/patches/tests/fs-local-link-f8.test.mjs` 19 项(含 `--asset` 直测资产本体),并登记进 `check-runtime-assets.mjs` 的 `BEHAVIOR_TESTS`。**教训**:退役理由「上游已原生覆盖」须逐站点核对——上游会往同一文件里继续加新的 link 站点。 | wen0531 | | 2026-09-22 | **修复:phone-control 预设在任何干净安装上恒「加载失败」(坑 170)** | `seedPhoneControlPreset` 对 `SKILL.md` 直接 `readText()`:文件缺席时抛 `FileNotFoundException`,被外层 `catch(Throwable)` 吞掉 ⇒ 函数当场返回,`agent.cordis.yml` / `preset.yml` / `customSkillDirs` 注入全都没跑;而幂等早退判据正是 `preset.yml` ⇒ 每次启动从同一行重炸、**永不自愈**。真机取证:logcat `seeding failed` + `open failed: ENOENT`(`EngineManager.kt:475`)、目录四层 mtime 纳秒相同(一次 `mkdirs()`)、同 uid 手写同路径成功(排除权限/属主/路径)。回归出处 = #141「SKILL 每次启动刷新」(其验证是「设备侧已热修」,前提是文件已在场 ⇒ 干净安装路径从未被测),#190 追加内容时同样只验了存量升级路径。修法:`if (skillFile.isFile) skillFile.readText() else null`,缺席视同需要刷新(对已有 SKILL.md 的设备语义逐字等价)。判据:`CallSiteContractTest.phoneControlSeedingToleratesAnAbsentSkillFile`(真源表达式在场 + 裸读形态消失,撤掉守卫即红)。 | wen0531 | | 2026-09-19 | **0.14.1 预览轮 / 三个 P0 设备缺陷修复(未提交/未验收,分支 feat/0141-preview)** | **事实收口**:`caps` 只随控制 op 回执抵达,`android_capabilities` 自己不入队 ⇒ 冷启动必然报「Shizuku 未就绪」而壳侧已授权(模型据此放弃可用能力)。修法:三态(就绪/实测未就绪/**未探测**)+ caps 缺席时引擎补探一次(`AndroidPrivilegeService.shizukuChannelProbed()`,TTL 10s,用不受档位门约束的 `vdInfo`;探测失败保持 undefined,**绝不降级成 false**)+ 三分文案(「未知」必须给出「直接试一次」的可执行动作);`android_privilege_status` 增独立特权通道行,`v.message` 不再挂「ADB 提示」标签;特权状态路由与工具同步补探。**工具面**:壳侧 `REAL_SCREEN_OPS` 从 11 条**收敛到 8 条**(`state`/`webSnapshot`/`webAction` 是 `A11Y_OPS` 的陈旧拷贝、都不读设备屏,却因不带 `screenId` 被范围门按真实屏拦死 ⇒ 缺省 virtual-only 下 android_web_dump、WebView ref 路径、点击生效校验全不可用);`android_act_input` 目标为虚拟屏时改走 `vdInput`(text 直传不受 ASCII 限制);`android_ui_tree` 删除它无法兑现的 `screenId` 参数;**补实现 `android_vdisplay_input`**(tap/swipe/keyevent/text,虚拟屏按键与文本此前在工具面无路可达;此前被三处文案与 Skill 文档承诺却从未 defineTool);虚拟屏组工具 3→4,注册集基线 16772→18490 B(初始可见集不变 2951 B,新工具受渐进披露掩蔽)。**落点可证**:`VdisplayController.launchApp` 增加落点回读(`am` 退出码 0 ≠ 落在目标屏,设备实测已有 task 时只打印 warning 仍回 0)——按 `Display #` 分组解析包的显式 ActivityRecord,三态回报(`vd-launched` / **`vd-launch-denied`** 带 landedDisplayIds / `vd-launched-unverified` 明写不构成证明);为避开 UserService 的 16 KiB stdout 截断(会恰好吃掉虚拟屏那一段)改用固定字面量先过滤(42 KB → 1.97 KB);引擎侧 schema 增 `screenId/displayId/landedDisplayIds/code`。**新门禁(3 条,均已接线两条链)**:`check-op-registry-parity.mjs`(跨语言 op 清单逐条相同)、`check-tool-name-promises.mjs`(指引承诺的工具名必须有声明位)、`verify-screen-scope-matrix.mjs`(设备套件:跨面一致性 + 落点回读 + 双屏像素对照 + real-only 反证,判据全落在设备事实上,证据不足判 INCONCLUSIVE 而非通过)。**判定性实测**:① `uiautomator dump --display ` **被收下但不生效**(无参/--display 2/--display 0 三者逐字节相同)⇒ 不得放开该家族,只修上层工具面;② `am start --display 2` 在本机对第三方应用有效,但已在真实屏有 task 时只打 warning 且退出码 0。**文档**:坑 161-165、known-gaps 三条更正、协调仓缺陷定性文档同步。 | kelai141 | | 2026-09-19 | **0.14.1 预览轮 / 三层验收约定(未提交/未验收,分支 feat/0141-preview)** | **测试规范升级(用户拍板)**:`AGENTS.md` §2.1 由「双轨」升级为**三层验收**——代码层 + CDP 层 + ADB 用户实际体验层,**缺一层即未验收**;**任一层红即这个系统有问题**,必落在四类之一(不符合人体交互逻辑 / 在用户视角里就是坏的 / 代码有问题 / 内部没打通),禁止当噪声、禁止只修那一层,并给三层的对照读法。新增**任务形态强制**:验收只给「目标 + 约束 + 成功标准」,**步骤必须由模型自己编排**,禁止把步骤一条条喂给它、禁止单步或两三步的简单操作;过程按工具调用序列 + 设备截图 + 最终汇报留证,**工具自报成功不算达成**。铁律 #2 与 §3 路由同步改口。详档 `docs/AGENTS/emulator-test-protocol.md` 同步:新增 §0 三层验收(对照表 + 四类读法 + 禁止只修红层)、§10 真实任务验收(形态/留证/反证),证据判据改三层、反模式清单补第 8-10 条。**动因**:0.14.1 三个 P0 缺陷(工具面报 Shizuku 未就绪、virtual-only 下工具大面积不可用、跨屏拉起报假成功)全部满足「代码层与 CDP 层都绿、只有真机用户层暴露」,定性见协调仓 `docs/0.14.1-preview-DEVICE-DEFECT-TRIAGE-AND-TEST-REFLECTION.md`。 | kelai141 | | 2026-09-19 | **0.14.1 预览轮 / 执行地图与模拟器测试规范(未提交/未验收,分支 feat/0141-preview)** | **执行地图**:新增 `docs/AGENTS/EXECUTION-MAP.md`(19 查点:主表 / 耦合矩阵 146 边 / 症状索引 101 条 / 疑点分级 26 条高危 / 20 张流程图 / 覆盖账本 184 项),门禁 `scripts/check-code-map.mjs`(覆盖 + 锚点 + 编号一致性,8 例判别力自检,`--no-parent` 可预演 CI 独立检出)并接入 apk CI。**模拟器测试规范**:`AGENTS.md` §2.1 强制双轨验收(CDP 断言 + adb 截图/点击)+ 详档 `docs/AGENTS/emulator-test-protocol.md`(原语/证据/判据/假失败清单/反模式)。**排查实修**:`jsString` 的 U+2028 转义是恒等替换(生产没修、单测因另一份 org.json 假绿)→ 改纯函数形态断言,9 例全绿;坑 159/160 登记。**漂移**:ARCHITECTURE.md 头部数字口径与已删除的 AdbState.kt 行、pr-gate 的 417→456、其余 50+ 条登记进地图 §8。 | kelai141 | | 2026-09-19 | **0.14.1 预览轮 / P0 真机 API 越级崩溃修复(未提交/未验收,分支 feat/0141-preview)** | **真机反馈(华为 NOH-AN00 / Android 31 / 出厂 0.14.0 vc39)**:`engine-died-during-boot` exit=1,logcat 12:55/12:56/12:57 三时点反复 `NoSuchMethodError: No interface method toList()Ljava/util/List; in class Ljava/util/stream/Stream;` → `SnapshotFs.deletePath(SnapshotFs.kt:50)` → `SnapshotTransaction.finish` → `EngineManager.applyRecovery`。**真因**:`Files.list(dir).use { it.toList() }` 的 `toList()` 是 **Java `Stream.toList()`(API 34)**而非 Kotlin stdlib 扩展;minSdk 26 → API<34 设备无此接口方法。**后果链**:`NoSuchMethodError` 是 **Error 不是 Exception**,`catch (e: Exception)` 抓不住 → 打穿恢复链 → **快照刷新/恢复在 Android<34 永久卡死(marker retained)**。**修法**:改用 `Files.newDirectoryStream(dir).use { it.toList() }`(`DirectoryStream` 是 `Iterable`+`Closeable`,Kotlin `toList()` 是 stdlib 扩展,无 API 级别依赖;`use` 保证关闭;语义等价:只列直接子项、不跟随符号链接、项读取失败记 onFailure 并跳过)。**全仓 API 审计(115 个 .kt)**:命中 1 处(即本坑),已修;`SnapshotUserData.kt:106` 的 `Stream.forEach`(since=24)与 `SnapshotTransaction.kt:240` 的 `File.listFiles()` 均判安全;全仓 23 处 `.toList()` 其余 22 处接收者为 Kotlin 集合/Sequence(安全)。**新增防线** `ApiLevelGuardTest`:禁用清单(`Stream.toList`/`ofNullable`/`dropWhile`/`takeWhile`、`Collectors.toUnmodifiable*`、`Files.readString/writeString/mismatch`、`String.strip*/stripIndent/formatted`、`List|Map|Set.of`、`requireNonNullElse`)+ **词法扫描器**(先剥字符串再剥注释,兼容跨行链式),带 13 项反证与零假红自检;**并修掉扫描器自身的自伤缺陷**(朴素「本行含块注释起始符」实现被 MIME 字面量误入块注释态、静默漏扫 3273 行/18%)。**注**:`isBlank()` 经 javap 对 `app/build/tmp/kotlin-classes` 实证解析为 `kotlin/text/StringsKt.isBlank`(stdlib,安全),**不**列入禁用以免大面积假红。**文档**:坑 153、modules.md、ARCHITECTURE.md。 | overlayui | | 2026-09-19 | **0.14.1 预览轮 / T5 块H+块I(未提交/未验收,分支 feat/0141-preview)** | **块I 悬浮球**:取消贴边强制吸附(删 `springSnapToEdge`/`cancelSpring`/`springAnim` 与 `androidx.dynamicanimation` 三个 import;壳侧该依赖从此零使用方,声明按详档判定保留并登记用途变化),保留最小边距并抽成唯一具名常量 `BALL_EDGE_MARGIN_DP=8`/`edgeMarginPx`(同时推导 `haloSizeDp`,整数恒等式被测试锁死),就地更正三处过期注释。新增状态描边 ring:`OverlayHalo.newHaloDrawable` 返回 **glow+ring 两层 `LayerDrawable`**(ring 硬边描边 2dp、内缩 4.5dp,落在球半宽与窗口半宽之间的可用环带内),`setHaloColors` 单函数同时写两层、颜色**同源**取自 `Halo.color`,`setHalo` 仍是唯一入口且脉冲逻辑一字未改(同 View → 天然同相位);`as? GradientDrawable ?: return@post` 式静默失败改为显式取层 + `LogCollector` 诊断;`Halo` 枚举改**纯 Kotlin ARGB 字面量**(原 `Color.argb` 在 `isReturnDefaultValues=true` 下恒 0 → 颜色类单测假绿)。**块H 悬浮窗**:完成态常驻文案「已完成,长按查看汇报」——权威信号 `api-session/status running=false` 置位、语义标签取 `.live.ndjson turn_end.kind`(失败/被阻塞/被中断不得显示「已完成」),完成位放**服务级** `CompletionNotice`(面板收起态存活),`showPanel()` 首次打开消费一次、收起即回常态、新一轮与 tool_call 清除;报告栏长按打开(新增 `OverlayReport.kt`:独立顶层窗口 + ScrollView,内容取 T6 窄接口 `NotifyStore.latestReportLine()` 并经既有 `NotifyStore.parseEntry` 解析,复用 `reportLine/reportBigText` 文案口径;空摘要/无数据给占位行仍可打开;hidePanel/onDestroy 两处收口);三击跳转(单一 OnTouchListener 状态机,长按优先、超 `scaledTouchSlop` 判拖动、`NEW_TASK\|SINGLE_TOP` 回 MainActivity 不重建 WebView);长按期间置 `panelOccupied` 守卫自动收起。**测试**:新增 `OverlayHaloInvariantTest`(四态互不相同/字面量逐字节等价/多 density 恒等式与 ring 环带)+ `OverlayCompletionNoticeTest`(A1 无 pending 普通完成必显示、复位反证、语义不伪装、A2 空摘要与无数据不崩、A3 二次点击与长按互斥),`CallSiteContractTest` 追加 15 条断红线。**门禁**:`check-kotlin-comments.mjs` PASSED。**文档**:坑 147-150、modules.md、ARCHITECTURE.md、BRIDGE-API.md、DEPENDENCIES.md。 | overlayui | | 2026-09-19 | **0.14.1 预览轮 / T6 块J+块L+块K壳侧 + 探针接线(未提交/未验收,分支 feat/0141-preview)** | **块J 通知实时化**:FIX-3 用户拍板取 A —— `DEFAULT_SUPPRESS_FOREGROUND = false`(前台也真发系统通知);存量迁移**不动 prefs**(旧抑制系默认值造出、`setSuppressForeground` 全仓零调用,改默认值即修好存量;显式值原样保留并备份留痕、schema 代次只跑一次)。FIX-1 抑制=延后:新增 `NotifySuppressQueue.kt`(TTL 5min/容量 8/单次补投 3/同会话覆盖式去重/自续 tick),补投复用 `deliverDeferred`。FIX-2 listener 真实实现(`ShellListener` + `installShellListener`,复用 `flashStatus`、只依赖既有公开面)。FIX-4 设置读写能力(`settingsSnapshot`/`applySetting`,写后读回、未知 key 拒绝)。**块L**:`writeBootDiag` 落 `files/boot-diag.log`;页面侧 fiber 状态不可得时显式 `pageSideRuntime=unavailable`(**绝不留空数组**)。**块K**:NSC base-config 撑开明文 + 回环保留;`check-manifest-hardening.mjs` NSC 段同批改新形态(显式声明/回环逐字比对/拒无 domain 明文口子,**实测回退即判红**)+ JVM `BrowserHostCleartextConsistencyTest` 守跨层同向;**#183 影响书面记录**。**探针接线**:真因 = 产出面缺席(`[perf] TOTAL` 仅测量 preload 产物,且不在快照/overlay/注入链、argv/env 均不含)+ 解析链挂在**默认关闭**的调试采集器上;修法与 C1「首个 HTTP 响应」口径见坑 144。**文档**:坑 144-146、modules.md、ANDROID-API-USAGE.md。**交付**:T5 窄接口 `NotifyStore.latestReportLine(): String?`。 | shellcore | | 2026-09-18 | **0.14.0 正式版(vc39,覆盖 v0.14.0-preview vc38)** | **本版主题:虚拟屏与 AI 浏览器达到可用线。** **虚拟屏**:跨屏拉起四条候选路径实测只有「壳侧 Shizuku UserService + `am start --display -n `」成立(monkey 无屏幕维度 / shell `am start` 不稳 / 进程内 `setLaunchDisplayId` 被 SafeActivityOptions 拒),据此把唯一可行路径做通;补 `screenId` 可发现性(此前该参数无 description,设备实录证明模型直接忽略 → App 落到真实屏);截图两条通道都认屏幕(ADB 回落此前完全忽略 screenId,抓的是真实屏——静默错误答案);等比 letterbox 按内容宽高比居中;可见性判据补「侧栏已收起」;序号复用(旧实现单调递增从不回收);会话隔离 + 销毁不设锁 + 10 分钟空闲回收;手动关机按钮。**AI 浏览器**:隔离宿主 WebView 可用;分辨率=CSS 视口(document-start 注入 + letterbox,innerWidth 精确);PC/手机身份;多页签;browser_snapshot 渲染每行 ref;侧栏收起时视口不再塌成 0x0(GONE → INVISIBLE + layoutDetached,实测 0x0→450x800);setInitialScale 在 API 36 stub 已移除 → 改注入实现。**附件入口分流(#215)**:回形针单击即出菜单(Tooltip 气泡插在按钮与 input 之间致兄弟相邻判据失效,改为结构判定);显式图片类型分流到系统相册(此前壳侧为修「空 accept 受限视图」把相册分支整个删掉,两个入口打开同一界面)。**Shizuku 时序**:ensureBound 只 await 一片 4s 而 bind 实测约 5s、且 bindLatch 一次性不清空 → 「第一次必失败第二次必成功」是确定结果;改为总预算内循环等待 + 三路径清 latch + connecting/not-bound 拆成两个 code。**快照升级健壮性**:deletePath 逐项容错 + 残留 stage 改名挪开(此前一个删不掉的损坏 stage 会让之后每次启动都失败)。**坑位 139-143 登记**(Termux 报错风暴真因 / 回形针兄弟判据 / Shizuku latch 时序 / 截图忽略 screenId / 图片 accept 被壳侧统一入口吞掉)。**验收**:门禁全绿、Kotlin 269 项 0 失败、插件 82+62+12、UI 180、双 ABI exit 0。 | kelai141 |