Repository navigation
fix: 第二/三档全量修复 + 仓库家务(12 个主题提交 + 1 个合并) - #28
Merged
Merged
Conversation
added 13 commits
October 15, 2026 04:11
组合期与状态机 - ChatScreen: expandedProcessIds 退出组合期 pipeline 依赖,改由 snapshotFlow 进入 combine,并用 frozenExpanded/frozenSplitIdx 判定能否复用冻结段;展开/折叠不再 触发整列表重建 [T-flatkeys-invariants] [T-android-round-collapse] [T-android-stream-pipeline-incremental] - StreamSessionController: publish 返回是否真正落地;trailing job 只在仍持有槽位 (st.trailingJob === currentJob)时清理 pending/trailingJob,避免慢 job 清掉新 job 的状态 [T-android-stream-flush-reentry] - ChatViewModel 系列: 锚点查询失败即中止而不是静默截断 [T-anchor-lookup-fail-closed], 删除按消息 id 定位 [T-delete-anchor-by-id],队列读改写走 CAS [T-android-queue-atomic-mutation], 恢复绘制互斥 [T-android-restore-paint-mutex],快照构造收敛到单一 helper [T-android-stream-snapshot-helper],SubAgent 的 write_paths 全非法时按“未提供” 处理以给出可执行报错 [T-write-guard-fail-closed] 解析与测量 - MessageTransformers: 未闭合围栏不再被当作 prose,修正 segStart 未推进导致的 StringIndexOutOfBoundsException(“begin 0, end 376, length 375”),闭合判定支持 ~~~ 与尾随空白 [T-think-tag-fence-open];新增 FenceAwareTagStripperTest 7 例 - KatexWebViewPool + katex-render.html: 每次渲染带令牌,迟到的 JS 回调与超时回收 按令牌判定,不再串台 [T-android-katex-render-token] 诚实性与线程 - ChatViewModelExecuteToolExt: code_graph 抛异常时返回 success=false,不再把错误 文本当成功结果喂回模型 [T-code-graph-honest-result] - ToolLoopDetector: 轮询类工具不再被误判成重复漂移 [T-poll-tool-drift] - SystemResourceMonitor: HUD 采样移出主线程 [T-android-hud-sample-off-main] - ChatViewModelSubAgentExt: 并行 worker 的投毒 writePaths 走显式报错路径 - MoveToSessionSheet: relativeDate 星期索引 off-by-one(“周一”显示在周二) - ChatIndicators: 分屏布局下的指示器修正 [T-android-split-chat] - ChatScreen: composer 麦克风开关状态收敛 [T-composer-mic-toggle] 死代码 - 删除 ChatFlatItems 无消费者字段 AssistantText / ProcessToolRef / processTools / foldableSteps,以及 ChatToolFormatting、StreamingFade、SessionMemorySheet 中的 孤儿辅助函数
- AppNavigation · deeplink 参数二次 URLDecode 遇到裸 `%` 直接崩溃 [T-android-double-decode-percent-crash] · 文件预览复用 WebView/holder 时的生命周期错绑 [T-android-filepreview-holder-lifetime] · 系统权限入口缺失(通知/电池优化/无障碍统一入口)[T-android-system-permissions-entry] · 启动期配置读取阻塞首帧 [T-android-startup-config-stall] · deeplink 后未重新播种导航状态 [T-android-deeplink-reseed] · 备份删除跑在已取消的 scope 上 [T-android-backup-delete-cancelled-scope] · 会话列表 SharedPreferences 读写移出主线程 [T-android-sessionlist-prefs-io] - SessionListScreen: 传入 Activity 的 ViewModel 泄漏 [T-android-activity-leak-sessionlist-vm], 行内 Flow 提升到 remember 避免每行订阅 [T-android-sessionrow-hoisted-flows], 暂停态徽标 [T-android-session-paused-badge] - ChatSplitScaffold: 会话选中逻辑收敛到唯一落点 [T-android-session-select-one-place] - SkillsManagementScreen: zip 导入的条目解析修正 [T-android-skill-import-zip] - SettingsScreen / ProviderListScreen / LogManagementScreen: 主线程 IO 与入口对齐 - LocaleWrap: 删除 minSdk 26 之下不可达的 config.locale 死分支 - 删除孤儿 OnboardingScreen(无任何导航引用,实际入口为 OnboardingModelSelectionScreen)
沙箱与宿主 - PRootKernel: 挂载守卫可被绕过路径 [T-mount-guard-bypass],失效的 handler 桩清理 [T-stale-handler-stubs],启动 splash 卡死 [T-android-startup-splash-hang] - TerminalSession: 会话级环境变量不再污染全局 [T-session-env-not-global], 启动卡死同源修复 [T-android-startup-splash-hang] - PersistentShell: offload prelude 只 source 一次 [T-offload-prelude-source-once] - WorkspaceMover: 逐个子目录隔离失败 [T-per-subdir-isolation],符号链接展开越界 [T-symlink-expansion] - SandboxHttpProxy: 隧道请求泄漏到宿主网络栈 [T-proxy-tunnel-leak] - ShellExecutor: 以当前进程注册表判定存活,取消后进程不再成孤儿 [T-current-process-registry] - ExecutionCoordinator: 每条命令独立文件写入,避免并发覆盖 [T-per-command-file-write] - FileChangeHub: computeIfAbsent 返回值与实际条目不一致 [T-computeifabsent-agreement] - SessionWorkspace: 分支视图统一到 canonical 视图 [T-branch-uses-canonical-view] - GuestWorkloadPolicy / ShellTimeoutPolicy / SandboxResourceGate: 三处重复的 setup 脚本判定合并为 isSetupScript() [T-setup-family],删除永不生效的 CPU 档位 / 超时档位 / 限额 / hang 策略常量表 [T-dead-cpu-tiers] [T-dead-timeout-tiers] [T-dead-limits] [T-dead-hangpolicy],磁盘占用百分比 在 ARM 上的换算修正 [T-disk-percent-arm] - offload: su 命令注入(引号)[T-su-exec-quoting]、Shizuku 超时钳制 [T-shizuku-timeout-clamp]、Shizuku 输出上界 [T-shizuku-bounded-output]、 无障碍 deadline 用 Duration 传递 [T-a11y-deadline-duration] - accessibility: 共享 executor 避免每命令建线程 [T-a11y-shared-executor], SkillReplayEngine 走宿主 shell 的正确入口 [T-a11y-host-shell] - GitCli: 临时文件命名碰撞导致并发提交互相覆盖 [T-git-commit-temp-collision] 工具层 - WritePathGuard / SubAgentRunner / SubAgentBrief: 解析失败一律投毒为“拒绝所有写入” [T-write-guard-fail-closed] - ToolOutputPolicy / ToolOutputSpill: 截断与落盘两套机制并存互相抵消,收敛为单一 机制 [T-tool-output-single-mechanism] - AtomicFileWrite: append 校验恒真是空断言 [T-append-verify-vacuous] - MultiEditTool / FileReadTool: 编辑项静默丢弃 [T-file-edit-silent-drop]、 单次读改写 [T-multi-edit-one-rmw]、大文件流式读取 [T-file-read-streaming] - WebFetchTool / KeylessSearchSources: 响应体读取加上界 [T-bounded-body-read], 热点正则提升到 companion [T-hoisted-regex] - AgentTools / MemoryTools / ExecuteCodeTool / DispatchAgentsTool: 记忆作用域漂移 [T-memory-scope-drift]、死 schema [T-memory-tools-dead-schema]、死 prompt 片段 [T-ui-skill-prompt-fragment-dead]、memory get 参数 [T-execute-code-memory-get]、 skills 契约 [T-dispatch-skills-contract] 测试: 新增/补齐 WritePathGuard、AtomicFileWrite、ToolOutput*、WorkspaceMover、 GuestWorkloadPolicy、KernelContract、SuCommand、GitCliParse 用例
- OAuth: 并发刷新互相覆盖 refresh_token [T-oauth-refresh-race](Claude/Gemini/Kimi/ XAI/通用五处统一走单飞刷新);回调服务器显式绑定 loopback,避免监听面扩大到 0.0.0.0 [T-oauth-loopback-binding];新增 OAuthCallbackServerHostTest - LLM 错误处理: 401 判定按 model 作用域收敛 [T-llm-error-401-model-scope], 错误分类不再把上游 4xx 归为可重试 [T-llm-error-classification], 异常文本中的 api key / bearer 统一脱敏 [T-llm-error-secret-mask], Anthropic SSE 的 error 事件不再被忽略 [T-anthropic-sse-error-event] - SSE: `data:`(无空格)前缀容忍 [T-sse-data-prefix-tolerance] - OpenAI: 视频轮询复用 create 返回的 URL 而非重拼 [T-video-poll-url-from-create]; Zen 免费车道跟随与 mimo 必带 thinking 的档位修正 [T-zen-free-lane-follow] [T-zen-mimo-must-think];ZenDisguise 序列化收敛为单点 [T-zen-disguise-single-serialize] - MCP: stdio 客户端在超时/异常下丢回复导致挂等 [T-mcp-stdio-reply-loss], 客户端重建时旧实例未关闭形成泄漏 [T-mcp-client-rebuild-leak] - NetworkMonitor: 陈旧连接的重试悬挂 [T-android-stale-conn-retry-hang] - ConfigBuiltins: 删除死配置键 [T-android-config-dead-key], thinking 档位与 thinking tokens 的架构关系收敛 [T-android-thinking-level-arch] [T-android-thinking-tokens-schema] - ProviderConfig / 翻译: 译名字段单一来源,去掉重复派生 [T-android-translation-single-source] - 死代码: provider/api 模块中无引用的 ProviderRegistry / ChatResponse / ProviderException,以及 app 侧仅剩 LegacyProviderAdapter 别名的 provider/api/ProviderAdapter 一并删除;模块保留仍在用的 ChatRequest / ModelProvider / ProviderCapabilities / StreamEvent
- ChatDao: clearBindingReferencing 的 LIKE needle 由裸 id 改为锚定 `"entryId":"<id>"`,此前清除 `inst/gpt-4` 会连带取消所有绑定 `inst/gpt-4o` 的会话 [T-android-binding-clear-anchor] - AppDatabase: 降级兼容(旧版本读到新版本 schema 不再直接崩) [T-android-downgrade-compat] - ProviderConfigMapping: 复合键碰撞 [T-android-composite-key-collision]、 重复 key 导致 agent loop 崩溃 [T-android-agentloop-dup-key-crash]、 枚举解码容错 [T-android-provider-enum-decode]、 译名字段单一来源 [T-android-translation-single-source] - ProviderRepository*/Save/Persist: 空加载不再覆盖既有配置(清库式写回) [T-android-provider-empty-load-wipe],save 移出主线程 [T-android-save-off-main-thread], 迁移到 Room 存储 [T-android-provider-room-store],对外列表做 COW [T-android-provider-emitted-list-cow],thinking 规则二期 [T-android-thinking-rules-phase2] - ChatRepository / MessageVersionDao: 归档删除分块执行 [T-archive-delete-chunked]、 版本索引批量建立 [T-archive-batch-version-index]、恢复预览列 [T-restore-preview-column] - SkillRepository: RMW 走 CAS [T-android-skill-rmw-cas]、内置技能版本门 [T-android-bundled-skill-version]、安全模式下 lateinit 崩溃(OpenMinis#147) [T-android-safemode-lateinit-crash-147]、启动期仓库 IO [T-android-startup-repo-io] [T-android-startup-splash-hang] - MCPRepository / EnvVarRepository: 启动期磁盘 IO 延后 [T-android-startup-repo-io] - Backup* : 恢复明文临时文件必删 [T-restore-plaintext-scratch], 删除跑在已取消 scope [T-android-backup-delete-cancelled-scope], 字节格式化收敛到一处 [T-android-human-bytes-one-place], SimpleDateFormat 每调用新建改为按 locale 缓存 [T-android-dateformat-per-call] - res/xml: 备份域名单一来源,避免 auto-backup 与 rules 不一致 [T-backup-domains] - 死代码: 删除无引用文件 data/DeviceIdentity.kt - 测试: 新增 ProviderConfigMappingRoundTripTest、BundledSkillUpgradeGateTest, 补 ChatConfigPrefsBindingTest
- AgentForegroundService: 中断后重新锚定通知/服务态 [T-fgs-reanchor] [T-android-interrupt],打断后不再出现“无暂停按钮”的死状态 [T-interrupt-no-pause-button],审批动作的 action 常量为单一来源 [T-approval-action-single-source] - ApprovalNotifier + ScheduledAgentRunner: 锁屏审批的可见性与点击回跳修正 [T-approval-lockscreen] - AppLogger: 写日志时的 `&` 转义/并发放大问题 [T-android-log-write-amp] - SessionConcurrencyManager: [T-STALL-DIAG] 诊断行从 println 改走 AppLogger, println 绕过轮转与脱敏且在 release 仍执行 - ToolOverlayController: 删除 minSdk 26 之上不可达的 SDK_INT 分支 - SecurityGate: 并入 PermissionIntersection 中仍在使用的判定后删除原文件 [T-permission-intersection-removal] - EvolutionEngine: 内置技能被演化回滚的保护 [T-evolution-bundled-rollback] - JschSshBackend: 阻塞式 reader 线程在断连后不退出 [T-ssh-blocking-readers] - OnlineApiTool: 响应体读取上界 [T-bounded-body-read] + 正则提升 [T-hoisted-regex] - debug/ChatMutationMethods + DebugInspectorMethods: 土耳其语环境下 lower/upper case 的 dotted-i 误判 [T-android-turkish-dotted-i] - BrowserUseManager: 更正指向已删除 FloatingToolStatusBar 的悬空注释 - 死代码: 删除无引用文件 service/BackgroundInterruptionTracker.kt
- TextToSpeechManager: 初始化回调不在主线程做重活 [T-android-tts-init-thread], 针对国产 ROM 的 TTS 引擎行为差异做兼容 [T-android-tts-rom-compat]; 新增 TtsQueueRejectionTest 覆盖队列拒绝路径 - VoiceTextSanitizer: 清洗正则提升到 companion,避免每次播报重编译 [T-android-voice-sanitizer-regex-hoist] - PhoneticNormalizer / VocabularyFilter: 土耳其语 dotted-I 归一化误判 (i/İ 折叠导致词表匹配失败)[T-android-turkish-dotted-i]
- 所有 guest 安装脚本 (minis-setup-*, minis-android-sdk-setup, minis-devsuite-offline-list): 下载产物一律校验和验证后才落地 [T-devsuite-verify];未通过即删除并报错,不再“先装后祈祷” - DevSuiteInstaller: apt/dpkg 与 guest 内其它 apt 进程争锁导致的半装状态, 改为串行化并复用锁 [T-devsuite-apt-lock];套件间依赖级联 [T-devsuite-cascade]; NDK 拆分下载 [T-devsuite-ndk-split];安装/进度写入幂等 [T-devsuite-progress-write];卸载按钮真正接上(DevSuiteSection 之前只清 UI 状态,不回收磁盘)[T-devsuite-uninstall] - offline.sh + minis-devsuite-offline-list: 离线导入改为显式开关 MINIS_ALLOW_OFFLINE,默认拒绝旁路校验 [T-devsuite-offline-consent] - DevSuiteSection: 外部工具链接跳转加确认 [T-dev-suite-link-consent], 进度显示单一来源 [T-devsuite-progress-write],卸载入口接通 [T-devsuite-uninstall] - DevSuite: 工具链接元数据补齐 [T-devsuite-tool-link] - 断点残留: `.part` 文件在启动与失败路径上清理并设上限 - 测试: DevSuiteCatalogTest 增加磁盘闸门用例 [T-devsuite-disk-gate], DevSuiteOrderTest 覆盖 apt 锁与级联顺序
体积与来源
- 删除 deps/lame-3.100(381 个受跟踪文件,其中 56 个是提交进仓库的 .o/.lo
编译产物)、deps/build_lame.sh、deps/build_ffmpeg.sh 与 deps/ffmpeg-patch:
全仓无任何调用者,MP4/MP3 转码走的是 guest 里的 ffmpeg/clamav 包
- deps/talloc **保留**:deps/build_proot.sh 仍在用,native-deps.yml 的缓存键
也引用它
- .gitignore: 用通用的 `deps/**/*.{o,lo,la,lai}` + `.libs/ .deps/` 取代只覆盖
autotools 配置产物的 LAME 专属清单;新增私钥材料
(*.pem/*.key/*.p12/*.pfx/id_rsa/id_ed25519)屏蔽;随构建器一起删除
deps/alpine/、deps/ffmpeg-6.1.2/、deps/lame-* 三条已无人写入的路径规则
scripts/
- 删除 15 个无调用点的孤儿脚本:iOS 时代遗留的本地化工具
(add_locale.py / add_localization.py / register_locale.py /
check_locale_batch.py,操作对象 src/ios/** 在本仓库不存在)、
已退休的 slim_android_cmdline_tools.py(SDK 宿主工具改为按需下载)、
safe_patch.py + safe_patch_selftest.py、collect-memory.sh、
symbolicate_hang.py(iOS .ips 符号化)、test_compact_orphan_toolcall.swift、
probe_zen_free_models.mjs、gen_debug_skill.sh(被 _android.sh 取代)、
install_alpine.sh / optimize_rootfs.sh / prepare_rootfs.sh(rootfs 制备已由
prepare_android_sandbox.sh 覆盖)
- 保留并修正 scripts/build_apk_aarch64.sh:它**不是**孤儿——guest 内的
minis-self-build 会 exec 它,BUILDING.md 也把它写成 aarch64 宿主的构建入口
- prepare_android_sandbox.sh / update_models_dev.sh / gen_debug_skill_android.sh:
路径与注释对齐(后者的 gen_debug_skill.sh 提法改成历史说明)
- 恢复被覆盖掉的执行位(guest 脚本与 scripts/ 下 shell 的 755)
CI(.github/workflows)
- android-apk.yml / native-deps.yml: `submodules: recursive` → `submodules: false`
+ 显式 `git submodule update --init deps/proot`,不再在每次 Android 构建里
克隆不编译的 iOS deps/ish
- android-apk.yml: 新增 Android SDK(cmdline-tools/platforms/build-tools/cmake/
ndk)缓存,键里带上每个钉住的版本,命中失败即 miss;cmdline tools 版本号提到
env;~/.gradle/wrapper/dists 一并缓存
- native-deps.yml: Go/NDK 缓存路径修正(paths 作用域下的缓存不会命中)
- pr-check.yml: 与上述一致的构建入口
Gradle
- app/build.gradle.kts: 移除零使用依赖 androidx.datastore:datastore-preferences、
com.squareup.okhttp3:okhttp-sse(流式响应是刻意手解析的)与
androidx.profileinstaller(仓库内不存在 baseline profile,接线缺失见
BUILDING.md,属 owner 决定)
- settings.gradle.kts: 说明 pluginManagement 块看不到脚本顶层 fun,
镜像清单必须保持两份同步 [T-gradle-script-plugin-mgmt-scope]
i18n / 资源
- 删除 20 个没有任何消费者(含 lint.xml / manifest / 反射名)的字符串键,
合计 220 条译文(17 个 locale 目录)
- Dev Suite 安装器硬编码中文文案改为 dev_suite_* 资源键并在 17 个 locale 补齐
文档与测试
- BUILDING.md / CONTRIBUTING.md / README.md: 更新克隆与子模块说明,新增
“调试变体整条链目前不可达”一节,把 debug 变体、ui-tooling、
stageDebugSkillAssets、.claude/skills/debug-server 悬空源列成 owner 决定项
- VendoredAssetIntegrityTest: 随退役资源改为 assumeTrue 休眠,并新增一条
**总是执行**的“仓库内不得再跟踪已退役 zip”断言
BUILDING.md / README.md / LINUX.md 把 `./scripts/prepare_android_sandbox.sh` 和 `./scripts/build_apk_aarch64.sh` 写成直接执行的入口,但仓库里这三个脚本的 mode 是 100644 —— 干净克隆后 `./xxx.sh` 会 Permission denied,只有显式 `bash xxx.sh` 才能跑(guest 里的 minis-self-build 正是这么绕过的,所以问题一直 没被发现)。scripts/update_models_dev.sh 同理。 只改 mode,不改内容;guest 资产脚本(assets/default_mount/usr/local/bin/*) 不需要处理,RootfsManager 解包时统一 setExecutable(true, false)。
#25 之后的复查里发现「按子串匹配 entryId」这条 bug 有两个同源实例,其中一个 上一批只修了一半: - ScheduledTaskStore.dropEntryRefs 用的是 `binding.contains(id)`,与 ChatDao 原来的 LIKE 完全同型:删除 `inst/gpt-4` 会连带取消所有绑定 `inst/gpt-4o` 的定时任务,用户选的模型静默丢失。改为解析绑定 JSON 的 `entryId` (与 ScheduledAgentRunner 读的是同一形状)后做集合等值比较——是等值判定,不再是子串。 - ChatDao.clearBindingReferencing 上一批把裸 id 的 `%…%` 换成锚定 `"entryId":"<id>"`,但谓词仍是 LIKE:模型 id 里合法的 `_` / `%` 会被当成 通配符,`inst/model_x` 仍能命中 `inst/modelYx`,等于换个写法重现同一个 over-match。改用 instr() 做字面子串定位,无通配符语义,NULL 绑定的行 instr 返回 NULL 即自然不匹配。 - ProviderRepository 里那段「此查询是 LIKE,需要精确匹配变体」的协调注释已经 过期(它描述的是修复前的状态),改写为记录两处现在的精确匹配契约,避免 下次有人照着旧注释把谓词改回去。 无单元覆盖:ScheduledTaskStore 需要 Context.getSharedPreferences,本仓库 单测是纯 JVM(无 Robolectric),不为此引入测试依赖;Room 侧的 instr 谓词由 KSP 在编译期校验 SQL 合法性。
effectiveBlocks 用 activeSubAgentToolIds 决定「某个子代理转录卡片现在是否还在 跑」——正在跑就先藏起来,跑完才作为卡片留下。也就是说它是冻结前缀的一个构建 输入,和 expandedProcessIds 同级;但 frozenReused 只比 splitIdx 与 expandedIds,于是存在一个窗口:整轮对话已经 drain(stream 空,splitIdx == msgs.size,后续不再有新 tick),此时某个子代理的 roster 收尾,splitIdx 与展开 集都没变 → 复用旧行 → 那本子代理转录卡片永远不出现。 - SubAgentActivityTracker.members 作为 combine 的第四路输入,parentToolId 集合 进 frozenReused 与构建参数。不能再复用组合期捕获的那个 val:它是 remember(subAgentMembers) 的结果,在整个 LaunchedEffect 生命周期里是同一个 冻结值,只有从 flow 里读才会随名单变化。 - 这个洞在 main 上也存在(frozenReused 只看 splitIdx),只是 main 把 expandedProcessIds 挂在 effect key 上,名单变化常常与展开集变化同时发生, 每次都会整表重建,把它意外盖住了。把 expandedProcessIds 改成 flow 输入 (本 PR 前一个提交)去掉了那次多余重建,也就去掉了这层偶然掩护,所以两处 必须一起改。 - StreamSessionController:把「trailingJob 的所有权判定依赖单线程约束」写成 注释——scope 是 viewModelScope(Main.immediate),tick 与 trailing job 天然串行, 所以 `st.trailingJob === coroutineContext[Job]` 成立;一旦这些状态搬到别的线程, 该判定就退化成数据竞争,需要 CAS 或 mutex。 本地::app:compileReleaseKotlin / :app:compileReleaseAndroidTestKotlin 通过, :app:testReleaseUnitTest 仍为 3003 tests / 2 个已知 Windows-only 失败。
main 在本分支开出来之后又前进了两个 PR,与这里重叠 20 个文件。17 个 strings.xml 与 MCPRepository / AgentForegroundService 都能干净自动合并,只有 DevSuiteStore.kt 真冲突:两边各自独立修了同一个「安装期 prefs 写风暴」—— - 这边(本 PR):按组件做 300ms 时间节流,进度行仍然落盘。 - main(#27):进度行**完全不落盘**,只有状态跃迁写盘(shouldPersistComponentState + encode/decodeComponentState),并把这条路径与 QueuedWork.waitToFinish() 逼主线程排空 → FGS createService 超时 → 安装期崩溃放大 的因果链写进了注释, 还带了 DevSuiteStoreThrottleTest 覆盖。 采用 main 的实现(更彻底、且已被上游测试钉住),删掉本分支这边因此失效的 PROGRESS_WRITE_INTERVAL_MS / lastPersistedMs / loaded 三个成员和相应注释; 保留本分支这侧仍然成立的两点:snapshot() 改为**合并**而不是整体覆盖 (进度行不再落盘之后,整体覆盖会把更新的内存进度行倒回旧值),以及 recoverInterrupted 的「安装被中断」文案走 dev_suite_interrupted 资源键。 合并后::app:compileReleaseKotlin / :app:compileReleaseAndroidTestKotlin 通过, :app:testReleaseUnitTest 3017 tests(含 #26/#27 新增的 14 条)/ 2 个已知 Windows-only 失败 / 4 skipped。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这个 PR 是什么
上一轮
#25修的是「最要命的 10 件事」。这一批把审查报告里的**第二档(High)、第三档(Medium/Low)以及所有「家务事」**一次做完,不跳过、不暂缓:deps/lame-3.100那 381 个受跟踪文件,含 56 个提交进仓库的.o/.lo编译产物)分组说明
fix(chat)expandedProcessIds退出组合期依赖);未闭合代码围栏导致的StringIndexOutOfBoundsException;KaTeX 截图串台(渲染令牌);流式 trailing job 竞态;code_graph失败不再伪装成功;星期标签 off-by-one;删除无消费者的折叠字段fix(ui)OnboardingScreen;LocaleWrap里 minSdk 26 之下不可达的死分支fix(sandbox)[T-setup-family]三处重复判定合并;删除四张永不生效的策略常量表;WritePathGuard投毒即拒绝;输出截断/落盘双机制冲突收敛为一种;大文件流式读取;热点正则提升fix(provider)error事件不再被吞;MCP stdio 丢回复与客户端重建泄漏;删除ProviderRegistry/ChatResponse/ProviderException/ProviderAdapter四个无引用抽象fix(data)clearBindingReferencing的 LIKE 未锚定(清inst/gpt-4会连带取消inst/gpt-4o的绑定);provider 配置空加载覆盖既有数据;复合键碰撞;恢复时明文临时文件残留;备份域名单一来源;SkillRepositoryRMW 走 CAS;启动期仓库 IO 延后;删除DeviceIdentityfix(system)SessionConcurrencyManager的println改走AppLogger(绕过轮转与脱敏且在 release 仍执行);土耳其语 dotted-i;删除PermissionIntersection(在用判定并入SecurityGate)与BackgroundInterruptionTrackerfix(speech)fix(dev-suite)MINIS_ALLOW_OFFLINE显式开关;.part残留清理与上限;卸载按钮真正回收磁盘;安装器硬编码中文文案进dev_suite_*资源(20 键 × 17 locale);外部工具链接加确认chore(hygiene)deps/lame-3.100+build_lame.sh+build_ffmpeg.sh+deps/ffmpeg-patch;删除 15 个无调用点的孤儿脚本;.gitignore用通用的deps/**/*.{o,lo,la}取代 LAME 专属清单并屏蔽私钥;CIsubmodules: false+ Android SDK/NDK/Gradle wrapper 缓存;移除零使用依赖(datastore、okhttp-sse、profileinstaller);删除 20 个死字符串键(220 条译文);文档与VendoredAssetIntegrityTest同步fix(data) 2/2ScheduledTaskStore.dropEntryRefs的binding.contains(id)(删inst/gpt-4会连带取消所有inst/gpt-4o的定时任务)改为解析绑定 JSON 后等值比较;ChatDao.clearBindingReferencing上一批锚定了"entryId":"<id>"但谓词仍是 LIKE,模型 id 里合法的_/%会被当通配符,换成instr()做字面定位;ProviderRepository里描述旧行为的协调注释一并改写chore(scripts)prepare_android_sandbox.sh/build_apk_aarch64.sh/update_models_dev.sh在仓库里是 100644,但文档写的是./scripts/xxx.sh——干净克隆后直接 Permission denied,一直没被发现是因为 guest 里是用bash显式跑的fix(chat) 2/2activeSubAgentToolIds是冻结前缀的构建输入(effectiveBlocks靠它决定「子代理转录卡片现在是否还在跑」),但复用判定只比splitIdx与展开集:整轮 drain 之后才收尾的子代理,两个条件都没变 → 复用旧行 → 那本转录卡片永远不出现。改为把SubAgentActivityTracker.members作为 combine 的第四路输入并纳入判定(组合期捕获的那个remember(subAgentMembers)值在整个 effect 生命周期里是冻结的,不能复用)。这个洞在 main 上也在,只是 main 把expandedProcessIds挂在 effect key 上、每次展开集变化都整表重建,把它偶然盖住了与 #26 / #27 的合并
main在本分支开出后又前进了两个 PR,与本分支重叠 20 个文件;17 个strings.xml和MCPRepository/AgentForegroundService都干净自动合并,只有DevSuiteStore.kt真冲突——两边各自独立修了同一个「安装期 prefs 写风暴」:main(fix(service): 安装期 FGS 崩溃三重根因——停启交叉、锚定缺口、prefs 风暴 #27):进度行完全不落盘,只有状态跃迁写盘,并把因果链(apply()排队 →QueuedWork.waitToFinish()逼主线程排空 → FGScreateService超时可观测为多秒停顿)写进注释,还带DevSuiteStoreThrottleTest。采用 main 的实现,删掉本分支因此失效的
PROGRESS_WRITE_INTERVAL_MS/lastPersistedMs/loaded与相应注释;保留本分支仍成立的两点:snapshot()改成合并而非整体覆盖(进度行不再落盘后,整体覆盖会把更新的内存进度行倒回旧值),以及recoverInterrupted的「安装被中断」文案走dev_suite_interrupted资源键。合并后重跑:3017 tests(含 #26/#27 新增 14 条)/ 2 个已知 Windows-only 失败。本地验证
最后一次提交(模型绑定精确匹配)之后重跑,结果一致。2 个失败是仅在 Windows 上出现的既有环境问题,与本 PR 无关:
MinisMediaResolutionTest的两条断言写成absolutePath.endsWith("minis-sessions/…")(正斜杠),在\分隔符的 Windows 上必然为 false;已在 Linux CI 上为绿。相关文件本 PR 未改动。审查中撤回的误报(记录在此,避免重复占用)
deps/talloc不是孤儿:deps/build_proot.sh在用,native-deps.yml的缓存键也引用它 → 保留。scripts/update_models_dev.sh+scripts/verify_models_dev_resolution.py是在用的维护脚本 → 保留(只补执行位)。MemoryScreenshotTiledView/SharedImageRow/TuiInlineRenderer与「FileObserver泄漏」几条无法复现:文件不存在,FileObserver只在sandbox/FileChangeHub.kt出现 → 撤回。mimo-v2.5分支经在线目录确认可达 → 保留但补注释。res/纳入搜索范围而把每个键的声明自身当成消费者,误报「0 个死字符串」;改为只搜消费侧后才拿到真实的 20 个。按项目设计保留、不再作为缺陷上报
filesDir/minis-global/mcp-servers/oauth/的明文 MCP OAuth token 桥,以及minis-global进入备份规则 —— 项目设计。src/android/release.keystore进仓库 —— 公开的默认上传证书(应用内更新按证书指纹比对)。私钥口令signing.properties已在#25移出仓库。需要 owner 决定的(本 PR 只标注,不擅自删/接)
build.gradle.kts的beforeVariants关掉了所有 debug 变体,于是debugImplementation(ui-tooling)、stageDebugSkillAssets、src/debug/assetssource set、以及.claude/skills/debug-server(本仓库没有.claude/,还被.gitignore忽略)全部永不触发。删掉还是接通?详见 BUILDING.md 新增小节。ScheduledAgentRunner → HeadlessChatRunner这条路径是活的,不只是「理论上的」。MinisDocumentsProvider的 manifest 注册是否保留。filescheme 的策略边界(浏览器工具允许/禁止读本地文件的取舍)。deps/ish子模块是否注销(本仓库不编译 iOS;CI 已改为不递归克隆)。profileinstaller依赖已随「无产物可安装」移除,若将来产出 profile,需与插件+CI 任务+侧载首启支持同批加回。.claude/skills/debug-server与gen_debug_skill_android.sh的「唯一真源」矛盾:现在是静默写出占位 SKILL.md,任务不失败。ThinkingRuleResolver把「凡 model id 以o开头」当作 OpenAI 原生 o 系列(startsWith("o") || startsWith("gpt-5"),规则ModelPattern("o*"))。olmo*/openchat*/opt*这类 id 会被无条件附上reasoning_effort,多数第三方兼容端点会 400。代码里(193–195 行)明确写了「这一期刻意保持与 iOS 逐字一致、不收窄」,所以属于你有意识保留的移植约束,而不是我擅自改的协议行为——要收窄成o+ 数字(o1/o3/o4)只需一处改动,随时可做。Android APK工作流在b3a2136(fix(security): Critical 批次加固——发布链、数据完整性、信任边界与 Dev Suite 授权 #25 刚合进 main 那次 push)失败在Upload keystore步骤——就是 fix(security): Critical 批次加固——发布链、数据完整性、信任边界与 Dev Suite 授权 #25 加的 fail-closed 闸门:既没有MINIS_UPLOAD_KEYSTORE_BASE64也没有MINIS_UPLOAD_STORE_PASSWORD时,拒绝退化成 debug 签名。随后 refactor(mcp): 注册表化 Dispatcher + 删除/刷新/工具开关修复 + 5 个零依赖内置 server #26 / fix(service): 安装期 FGS 崩溃三重根因——停启交叉、锚定缺口、prefs 风暴 #27 的两次 main push 都是绿的,说明这期间补上了某个 secret。闸门按设计生效过,不是回归;但建议确认配的是哪一个:只有MINIS_UPLOAD_STORE_PASSWORD的话,等于「口令 + 仓库里那张已公开的证书」齐活就能签发升级包——私钥本来就是公开的,口令是唯一变量;MINIS_UPLOAD_KEYSTORE_BASE64(真正的私有证书)才撑得住这条链。这属于你的证书轮换决定,我没有动。合并
按约定:CI(Compile / Unit tests / L10n gate)为绿后合入
main。