Skip to content

[维护者确认] PR #5252 feat(mobile): 复用电脑订阅授权并新增额度小组件(iOS优先) #5401

Open

Description

@MagicLizi

本次改动

  • PR:feat(mobile): 复用电脑订阅授权并新增额度小组件(iOS优先) #5252 feat(mobile): 复用电脑订阅授权并新增额度小组件(iOS优先),head 4d065b299d79d14e280113bfe70c118145eba328(base main @ 88e224475a6183f7b31218a7499be956a4bf2667)。
  • 内容:手机选择已连接的电脑后,复用 Cindy 已有 Claude / Codex / Grok 授权,把脱敏订阅额度快照交给桌面小组件;不另配供应商授权、不保存 token / 邮箱 / providerId / 账号指纹。含设置入口、device-link 只读同步、v2 脱敏快照与逐窗口观测时间,iOS WidgetKit(三张 small 卡 + medium 组合卡)与 Android RemoteViews 桌面小组件(模块 apps/mobile/modules/cindy-quota-widget/**),以及配套资源、翻译与测试。
  • 范围:apps/mobile/**(app.json、app/_layout.tsx、app/settings.tsx、app/subscription-widgets.tsx、src/widgets/、fingerprint.config.cjs、modules/cindy-quota-widget/)、docs/design-rules/design-inventory.md 等,整体约 4965 行级改动。

为什么需要维护者确认

本 PR 同时命中五类确认门,其中 security 与 coldUpdate 两类不接受 bugfix / 小改动的语义豁免,自动流程无法自行放行:

  • product:feat 类型 + 命中 UI 路径(apps/mobile/app.json、app/_layout.tsx、app/settings.tsx 等)。
  • security:security-sensitive —— 危险写法 13 处(dynamic-eval / shell-string-exec),涉及用户隐私与系统安全,需维护者明确确认。
  • rules:改到审查规则文档 docs/design-rules/design-inventory.md(审查判据自身变更)。
  • coldUpdate:改到 mobile runtime fingerprint 输入(app.json / fingerprint.config.cjs / cindy-quota-widget 原生模块等),且没有 guard 结论,需确认是否触发冷更。
  • arch:核心路径改动 172 行(≥ 150)+ 整体 4965 行(≥ 800)。

请维护者确认

请维护者直接在 PR 上 Approve 表示确认通过;如果需要作者修改,请在 PR 上 Request Changes 并写明要求,作者改完后请重新审阅。确认完成前,自动流程不会合并该 PR。


关联 PR:#5252(作者 @FicoHub);本 issue 由 review-pr 流程自动创建,用于先讨论该 PR 涉及的产品 / UI 变更,维护者确认后 PR 会恢复推进。

Activity

  1. dash-s-cindy commented on Oct 3, 2026

    @dash-s-cindy

    判断:将电脑端订阅额度投影到手机桌面小组件,具有减少查询步骤的独立用户价值,值得继续推进。现阶段建议有条件地继续审阅,不直接批准合并:已有额度读取能力可以复用,但清理失败路径、Android 布局边界及冷更交付仍需收敛。

    行动:作者先为以下两个静态可确认的问题增加失败用例并修正;维护者并行核对安全扫描明细、产品范围和冷更方案。修订后以新 head 的 CI、原生构建和设备验收结果作最终审阅决定,不能用流程确认替代技术验收。

    现有能力与本次增量

    对当前 head 的审查意见核验

    以下检查针对 4d065b299d79d14e280113bfe70c118145eba328 的公开源码,属于静态验证,未执行测试或设备复现。

    1. 停止共享后,清理失败无法通过同一操作重试

    定位:apps/mobile/src/widgets/quotaWidgetController.ts 的 clearNative()、selectDevice()。

    clearNative() 捕获原生删除异常后返回 false;selectDevice(null) 仍将 deviceId 更新并持久化为 null。下一次 selectDevice(null) 命中同值提前返回,不再调用清理。Android 原生 QuotaSnapshot.clear() 在删除后检查文件是否仍存在,确有可传播的失败分支。

    这证明清理失败状态与“已经停止共享”的选择状态可能不一致,不证明用户设备已发生泄露。建议修正为可重试、可收敛的清理流程,避免同值判断吞掉未完成清理;停止后仍应阻止旧在途响应重新写入。

    验收:注入第一次原生清理失败,随后重试成功;覆盖停止共享、退出/换用户及设备撤销。验证最终缓存为空、全部 widget kinds 收到刷新请求,旧 epoch 的响应不能恢复旧数据。系统收到刷新请求不等于立即完成桌面重绘,设备验收需区分两者。

    2. Android 提示预留会把可显示卡片的容量算成零

    定位:apps/mobile/modules/cindy-quota-widget/android/src/main/java/expo/modules/cindyquotawidget/QuotaWidgetPresentation.kt 的 layout()。

    在 width=356、height=164、fontScale=1、providers=3 时:两列成立,初始容量为2;因第三家放不下,预留20dp提示;重新计算为 floor(144/164) × 2 = 0,返回零张卡及空间不足状态。此计算支持 PR 审查中的边界问题,但尚未进行 Launcher 运行复现。

    建议调整提示与卡片的空间分配,使提示不会反向消灭原本能完整显示的卡片,并补纯函数回归及实际 Launcher 验收。覆盖该尺寸附近、1至3家供应商、窄布局和系统字体放大,不以缩字或截断掩盖溢出。

    3. 单家临时读取失败会覆盖旧额度,需要先确认展示语义

    定位:apps/mobile/src/widgets/readWidgetQuota.ts 的 readWidgetQuota(),以及控制器的 refresh()。

    各账号读取异常被转换为空额度行,随后整份结果写入原生缓存;因此,一家暂时失败、其他家成功时,失败方的旧值会被覆盖。这不同于整个读取抛错时保留旧快照并调用 offline() 的路径,也不同于 main 任务菜单对部分暂时失败保留旧值、标记失效的既有意图。

    这条行为值得讨论,但尚无用户故障证据,也不能据此直接规定必须保留缓存。请先确认小组件是否应保留同一账号最后一次观察并明确标示失效。若选择保留,必须保留原始逐窗口观测时间,不把失败或重绘当成新观察,并通过当前账号身份校验;账号切换、撤销或移除不能复用旧值。账号信息不应因此进入 OS 快照。

    五类确认门应如何落地

    • 产品/架构:确认增量是系统小组件,而非新增 OAuth。widgetAccounts() 当前按宿主顺序每个平台选一个已连接账号;请明确这一展示范围与设置说明是否符合本期产品意图,不据此限制现有任务菜单的多账号行为。
    • 安全:“dynamic-eval / shell-string-exec 13处”目前只有流程报告,未取得逐条扫描定位和完整 diff,不能认定为13个漏洞,也不能宣布安全审查完成。需要逐条核对是否新增、是否生产可达、是否有不可信输入进入执行边界。已检查的 quotaSnapshot.ts 和 Android QuotaSnapshot.kt 重新构造字段白名单,为不把 token、邮箱、providerId 等写入 OS 快照提供了源码证据;这不替代完整数据流审计。
    • 规则:main 的 docs/design-rules/design-inventory.md 明确区分 GENERATED 机器事实和人工决策区。PR 正文称冲突按生成结果解决。因此,修改台账不自动等于修改审查判据;需检查 diff 是否改变人工决策、规范或豁免,而不是仅依据目录归类下结论。
    • 冷更:head 的 fingerprint.config.cjs 显式新增 modules/cindy-quota-widget/widget 为 fingerprint 输入,原生模块已新增,PR 正文也明确要求冷更。建议补正式 fingerprint/guard 结论及对应平台原生构建,验证旧原生安装不会收到不兼容 OTA;不应为绕过确认而忽略原生源或覆盖 runtime。

    合并与发布证据仍需补齐

    作者报告当前 head 有1142项定向测试及三个包类型检查通过;这些是作者提交的验证记录,不是本次重新执行的结果。正文记录的新 head CI 当时仍在排队,现有只读记录没有提供完成结果,不能引用旧 head 的失败或通过替代当前结果。

    合并审阅需要确认新 head required checks 和上述回归用例;发布验收还需补合入最新 main 后的完整原生重建、冷更签名/安装、最新宿主的真实 Plus/Pro 套餐联调,以及声明支持范围内的设备和账号验证。历史 iOS/Android 构建、模拟器截图与短时 Doze 证据不能自动覆盖当前 head;Grok 真实账号、Android 实体机/OEM及不同真实账号仍有明确证据缺口。

    小组件刷新文案应保持 PR 已声明的边界:前台同步获取源数据,后台 timeline/调度主要重绘缓存,不承诺固定时间获得最新供应商额度。没有证据要求新增后台保活、新授权或付费探测。

    相关记录

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions