Adversarial Review · Fable 5 High Reasoning · 12 agents

Today's native fixes, re-judged from first principles 今日原生端修复 · 第一性原理对抗性复审 · 2026-08-01

Every issue fixed today across the 5 native repos (Android / Android TV / iOS / tvOS / Windows) was independently re-reviewed against the business big picture — not just the diff. 全部12项修复逐一复审:不止看代码,更质询其商业逻辑与失败语义是否符合中国大陆网络现实。

12fixes reviewed · 项修复复审
6critical findings · 致命发现
18high findings · 高危发现
47business-logic flaws · 业务逻辑缺陷
12invariant checks passed · 产品铁律全过

Verdicts: 2 sound · 6 sound-with-risks · 3 flawed · 1 wrong-approach  |  结论分布:2 稳健 · 6 稳健有险 · 3 有缺陷 · 1 方向错误

One bug class dominates: inverted failure semantics 主导性缺陷模式:失败语义被反转 —— 冗余被当成合取条件

Exactly the class you flagged. Mirrors exist for robustness: one success = success. Four of today's fixes inverted this into "any failure = failure" — or fail-closed teardowns on transient noise. In CN, where one dead mirror is Tuesday, that converts routine network weather into outages, lockouts and forced re-logins. 四项修复(AND-13 / AND-11 / TV-8 / WIN-14)把「任一镜像成功即成功」反转为「任一镜像失败即失败」,或对瞬时故障做失败关闭式拆除。在墙内,单镜像故障是常态——这等于把日常网络波动变成事故。

Correct: redundancy is fail-open 正确:冗余即容错

Parallel racing → first valid success wins · 并行竞速,首个有效成功即胜出
App 镜像 A ✓ 镜像 B ✕ 镜像 C ✓
✓ CONNECTED — one mirror dead, user never notices 照常连接,用户无感

Shipped today ×4: fail-closed conjunction 今日×4:全镜像合取

ALL mirrors must succeed / one veto kills the result · 要求全部成功,单一失败一票否决
App 镜像 A ✓ 镜像 B ✕ 镜像 C ✓
✕ FAILED — teardown / lockout / forced re-login 拆隧道 · 锁购买 · 踢回登录

Where it shipped today · 今日出现位置: TV-8 one poisoned mirror blocks all CN logins   AND-11 plan catalog & order polling need all-mirror unanimity   WIN-14 frozen origin allowlist + deleted failover layers   AND-13 single-mirror 403 → durable account invalidation.
The mirror image, done right · 同类问题的正确修法: APN-59 TV-11 relaxed over-strict exact-match provisioning checks to superset containment — the profile is an authorization superset, the signed binary stays exact. Same first principle, applied correctly. 描述文件本就是授权超集,改为包含校验、二进制仍精确校验——同一原理的正确应用。

Scoreboard 十二项修复评分(1–100,90+可直接发布)

APN-59iOS release
profile validation fix + v2.0.5 · 描述文件校验修复
92
TV-11tvOS CI
entitlement superset + v2.0.4 · 授权超集放宽
90
HUB-CI5 repos
Issue Hub wiring · 统一议题中枢接入
84
WIN-CEOunmerged
origin allowlist v2 + UI remap · 订阅来源白名单v2
80
ATV-8Android TV
password reset · 密码找回
76
APN-56iOS
UI v2 → main integration · UI v2 合入主干
74
AND-12Android
password reset · 密码找回
72
ATV-7Android TV
keystore removal + gates · 签名材料清除与门禁
65
AND-13Android
manual/durable node refresh · 手动持久节点刷新
58
AND-11Android
IAP purchase/restore · 应用内购买修复
30
WIN-14Windows
node refresh & cache · 节点刷新与缓存一致性
30
TV-8tvOS
quota + tunnel contract · 流量配额与隧道契约
25
sound 稳健 (≥90) sound-with-risks 稳健有险 (60–89) flawed / wrong-approach 有缺陷 / 方向错误 (<60)

Live exposure right now 当前线上风险敞口

P0
tvOS v2.0.4 was tagged & pipelined TODAY containing TV-8 (score 25) — hold the rollout. tvOS v2.0.4 今日已打标并跑通发布流水线,内含 TV-8(25分)——建议立即暂停推送。

Foregrounding the app stops a healthy streaming tunnel and never restarts it; one GFW-poisoned mirror vetoes healthy mirrors and blocks all CN logins; one CDN 502 wipes the device master key, permanently bricking the encrypted node cache; auto-reconnect removed entirely. 前台切换即断流不复连;单坏镜像否决登录;单次502即销毁主密钥;彻底移除自动重连。

P0
Windows fielded fleet is still broken — the fix (WIN-CEO branch) only helps future builds. Server-side mitigation needed now. Windows 已部署客户端仍处故障态——补丁只救新版本。需立刻做服务端过渡。

WIN-14's frozen 18-host origin allowlist caused a same-day outage when the backend rotated the subscribe domain: paying users see a 购买套餐 upsell card instead of nodes. Until update adoption: point the panel's subscribe_url back at an approved v1 origin. 面板应先把 subscribe_url 切回 v1 白名单内域名,直至新版覆盖率达标。

P0
Android main (unreleased) carries permanent-lockout paths — do not cut a release from it. Android 主干(未发版)含永久锁死路径——修复前不得据此出版本。

AND-11: process death between order-journal write and POST bricks purchase AND logout forever (its own consultation doc flagged this P1 and it merged anyway); an ambiguous account-deletion timeout locks the whole app behind a non-dismissible screen. AND-13: post-upgrade first launch with flaky network silently refuses Connect despite a valid stored config. 订单日志落盘后进程被杀→购买与退出永久锁死;注销超时→全屏死锁;升级首启弱网→静默拒连。

Issue-by-issue 逐项复审结论(仅列关键发现)

🤖 vpncheap-android

merged to main · not yet released 已合入未发版
#13 · Manual & durable node refresh节点刷新改手动 + 持久化
FLAWED58
  • HIGHSingle mirror JSON-403 → durable account invalidation: tunnel teardown, config wipe, forced re-login. Xboard returns the same envelope for a mere subscribe-token rotation.单镜像403即判账号失效:断隧道、清配置、强制重登。Xboard 对订阅令牌轮换返回同样的403信封。
  • HIGHConnect gate starts barred: post-upgrade first launch offline → valid stored config silently refuses to connect (button & QS tile).连接门初始关闭:升级首启弱网时,按钮与磁贴静默拒连。
  • HIGHAll-or-nothing parser: one duplicate node name or bad port in the panel rejects the entire catalogue, fleet-wide, Android only.解析全有或全无:面板一个重名/坏端口即全端拒收整份目录。
  • GOODDurability mechanics + encryption + label sanitization are genuinely solid; invariants improved.持久化机制、加密与标签脱敏扎实,产品铁律反而增强。
#12 · Native password reset原生找回密码流程
RISKS72
  • HIGHJournal never cleared on success: process death after a successful code-send re-locks the flow as "outcome unknown" — fatal on low-RAM CN devices where switching to the mail app kills the process.发码成功后日志不清除:切邮箱App进程被杀,回来表单被锁为「结果未知」。
  • HIGHPre-dispatch connect failures classed as outcome-unknown with no mirror failover — over-fail-closed for CN.请求未发出的连接失败也判「结果未知」且无镜像回退。
  • GOODContract verified against live prod; one-shot POST transport & secret hygiene excellent.与线上契约逐字段核对;一次性传输与密钥卫生优秀。
#11 · In-app purchase & restore fix应用内购买与恢复修复
FLAWED30
  • CRITPlan catalog & payment methods require ALL-mirror success — one dead AWS Lambda mirror disables purchase despite a valid catalog in hand. The exact inverted-fallback bug class.套餐/支付方式要求全镜像成功——手握有效目录仍禁购。教科书级反转缺陷。
  • CRITOrder-status polling needs unanimity: user pays via Alipay, one flaky mirror → "contact support", money taken, then new purchases AND logout blocked.订单轮询要求全镜像一致:已付款却确认失败,进而锁死新购与退出。
  • CRITIdentity-mismatch latch fires on transport garbage (captive portal HTML) → durable force-kick to login on every cold start.酒店门户注入的HTML即触发持久身份闩锁,冷启动被踢回登录。
  • HIGHKnown-P1 shipped: crash between journal write and POST permanently bricks purchase + logout on the device. Plus a 1167-line out-of-scope VPN-service rewrite inside a purchase PR.自评P1仍合入:崩溃时机不巧即永久锁死;购买PR夹带千行VPN服务重写。

📺 vpncheap-android-tv

merged to main 已合入主干
#8 · Password reset (D-pad)电视端密码找回
RISKS76
  • HIGHPre-dispatch failures lock the form and wipe minutes of D-pad input — a GFW RST before transmission shouldn't be "outcome unknown".未发出的请求失败即锁表单、清空遥控器辛苦输入的内容。
  • MEDLatent: unguarded WebView crashes captcha on WebView-less CN TV boxes; challenge_url origin check breaks if captcha is ever enabled.潜伏:无WebView盒子直接崩溃;开启验证码后源校验会整体拒绝。
  • GOODHappy path solid, contract verified against all 3 prod mirrors, strong tests.主路径扎实,三镜像实测验证。
#7 · Remove committed keystore + security gates清除仓库签名材料 + 安全门禁
RISKS65
  • HIGHThe real root cause is deferred: keystore + credentials remain recoverable from git history; no rotation, no revocation — and the keystore is shared with the Flutter repo.根因仅延后:密钥仍可从历史恢复,未轮换未吊销,且与Flutter仓库共用。
  • HIGHNo branch protection on main — the whole trusted-workflow model is unenforced; the gate is also flaky-red (full Gradle builds via aliyun mirrors from US runners), training people to ignore it.main无分支保护,模型形同虚设;门禁经aliyun跑Gradle常态误红,会养成无视红灯的习惯。
  • LOW~9,100 lines of bespoke hardened bash for a 167-file private repo — disproportionate maintenance load.9100行定制bash,维护成本与威胁模型不成比例。

📱 vpncheap-apple-native (iOS)

v2.0.5 tagged · TestFlight 已打标
#56 · UI v2 → main integration (+42.8k lines)UI v2 合入主干与加固
RISKS74
  • HIGHNodesV2 pingAll re-introduces the all-node probe fan-out — the documented device-limit reap landmine (2-min disconnects) deliberately removed from Flutter in PR #55.全节点测速重新引入设备数风控雷区——Flutter端曾专门移除的同类扇出。
  • MEDFailed hot reload leaves a zombie "connected" tunnel; IP-redaction made the PAC→Global fallback dead code; shipped V2 UI silently swallows all connect errors.热重载失败留僵尸连接;脱敏令PAC回退失效;V2界面吞掉全部连接报错。
  • GOODAES-GCM config envelopes, auth-generation fencing, 300ms terminal-state dwell — exceptional concurrency work. Never smoke-tested on a signed device (G7 waived).加密信封与并发防护出色;但真机NE冒烟被豁免。
#59 · Provisioning-profile validation + v2.0.5描述文件校验修复并升版
SOUND92
  • GOODTextbook re-layering: profile checked as authorization superset, signed binary stays exact-checked. The correct inverse of today's dominant bug class.教科书式分层:描述文件按超集校验,二进制仍精确校验——正是主导缺陷类的正确解法。

🖥 vpncheap-apple-tv (tvOS)

v2.0.4 tagged TODAY · 今日已打标
#8 · Strict traffic quota + tunnel authorization严格流量配额与隧道授权契约
WRONG-APPROACH25
  • CRITForeground/refresh stops a healthy connected tunnel and never restarts it — check status mid-Netflix, stream dies.前台/刷新即停掉健康隧道且不重启——看剧中途瞄一眼App,流断了。
  • CRITOne bad mirror vetoes login for everyone: GFW DNS-poisons the .top domain (routine) → no user in China can log in, despite two healthy Lambda mirrors.单镜像被污染即全体无法登录——两个健康镜像的成功被一票否决。
  • HIGHOne CDN 502 wipes the device master key → encrypted node cache permanently undecryptable. Auto-reconnect removed: Wi-Fi blips now stay disconnected. Post-login follow-up errors clear a just-saved valid token.单次502即销毁主密钥、缓存永久报废;移除自动重连;登录后续错误清掉刚存的有效令牌。
  • GOODQuota is server-owned ground truth (X-Subscribe-Status already enforced) — client needed best-effort revalidation, not this. Encryption/invariant-1 work is good and worth salvaging.配额真值在服务端,客户端只需尽力校验;加密与铁律1加固值得保留。
#11+#12 · CI entitlement superset + v2.0.4 bumpCI授权超集放宽 + 升版
SOUND90
  • GOODCorrect semantics at the correct layer (profile ⊇ needs, product == intent). Minor: entitlement-key whitelist still exact — same blockage class can recur.语义与分层正确;残留:授权键白名单仍精确,同类阻塞可能复发。

🪟 vpncheap-windows

#14 merged · outage same day · WIN-CEO 分支未合入
#14 · Node refresh & cache consistency rewrite节点刷新与缓存一致性重写(约8000行)
FLAWED30
  • CRITFrozen 18-host subscribe-origin allowlist caused a production outage within 12h when the backend did a routine anti-blocking domain rotation — paying users shown a purchase card.硬编码订阅白名单:后端例行换域12小时内引发线上事故,付费用户看到「购买套餐」。
  • HIGHThree CN-resilience layers deleted: fresh-login retry backoff (its guardian tests rewritten to expect "stay empty"!), API-domain remount failover, token-path fallback, 30-min cache TTL.删除三层墙内容灾:登录重试退避(守护测试被改成期待空列表)、域名重挂、token兜底、30分钟TTL。
  • LOWMerged with admittedly incomplete review, zero Windows runtime verification (macOS host can't build net48). CAS/atomic-cache engineering itself is good.评审未完成、零Windows实测即合入;CAS与原子缓存机制本身优秀。
fix/ceo-node-origin · Allowlist v2 + UI remap (unmerged)订阅来源白名单v2 + 界面纠错(未合入)
RISKS80
  • HIGHFix strands the fielded fleet — v1 clients stay broken until users update. Required server-side transition (serve subscribe_url from an approved v1 origin) is unstated anywhere.只救新版本:已部署客户端持续故障,必需的服务端过渡措施未见于PR。
  • MEDOrigin-rotation treadmill remains: every future rotation needs an app release. Needs a signed runtime policy-update channel. Nothing compiled on Windows yet (CI round-trip needed — this bit PR #91→#92 before).换域跑步机未解:应建签名的运行时策略通道;net48尚未在Windows编译验证。
  • GOODMinimal, well-tested, kills the harmful "buy a plan" misclassification; cache fallback preserved. Merge after the ops plan exists.改动小而准,修复误导性购买卡;配好服务端过渡后可合入。

🔗 Cross-repo · Issue Hub CI

5 repos + pin roll 五仓接入与pin滚动
Unified Issue Hub wiring + Chinese triage pin roll统一议题中枢接入 + 中文分诊规则
RISKS84
  • MEDAccount-wide classic PAT exposed to issue-triggered runs incl. a public repo; pin rolls are unreviewed direct pushes to unprotected branches — the exact channel a hub compromise would use. Label fan-out self-triggers ~8–10 runs per issue.账号级PAT暴露于议题触发流;pin滚动无评审直推;标签自触发扇出每题约8–10次运行。
  • GOODFull-SHA pin identical across 6 repos, today's roll verified config-only (zero code delta), no fork-PR exposure, deterministic (no LLM in the correctness path).全SHA锁定核验一致;本次滚动纯配置;无fork暴露;正确性路径无LLM。

Recommended action queue 建议行动队列

1
Hold tvOS 2.0.4; revert TV-8's enforcement layer, keep its encryption.暂停tvOS 2.0.4;回退TV-8执法层,保留加密加固。

Any-success mirror arbitration; revalidate without stopping the tunnel; key deletion only on logout; restore on-demand reconnect.

2
Windows ops action today: repoint panel subscribe_url to a v1-approved origin.今日服务端行动:面板subscribe_url切回v1白名单域名。

Then merge WIN-CEO after a Windows CI build; plan the signed runtime origin-policy channel to end the rotation treadmill.

3
Android: restore any-success reads in AND-11; unbar stored-config connect + per-node skip parsing in AND-13.安卓:AND-11恢复任一成功语义;AND-13放行已存配置连接、解析改逐节点跳过。

Also: clear the mutation journal on definite success (AND-12), never latch identity from transport garbage.

4
iOS: remove/gate NodesV2 pingAll before releasing 2.0.5 to users.iOS:发布前移除或旗标全节点测速,避免设备数风控雷区。

Also restore visible connect errors in the V2 UI and run one signed-device NE smoke.

5
Rotate the leaked Android keystore (shared with Flutter repo); enable branch protection; split flaky Gradle meta-tests out of the gate.轮换泄露的签名密钥(与Flutter共用);开启分支保护;拆分易误红的Gradle元测试。

History still contains the keystore + credentials; containment currently rests on repo privacy alone.

6
Codify the suite-wide failure-semantics contract in CLAUDE.md.把全套件失败语义契约写入CLAUDE.md。

"Reads: first valid success wins. Redundancy is never conjunctive. Fail-closed teardown only on authoritative business verdicts, never on transport noise." Four independent fixes re-invented the same inversion in one day — that's a missing written contract, not four careless authors. 一天内四处独立复发同一反转——缺的是成文契约,不是个别疏忽。

What held up well · 值得肯定

All four product invariants (ingress-IP secrecy, no protocol in UI, list-only nodes, liquid glass) passed in all 12 reviews — several fixes actively strengthened them (encrypted caches, label sanitization, log redaction). The low-level engineering is consistently strong: atomic encrypted persistence, one-shot POST transports, typed state machines, SHA-pinned CI. Today's failures are almost entirely policy-layer — what to do when something fails — not mechanics. 四条产品铁律12项全过,多项反而增强(缓存加密、标签脱敏、日志去址)。底层工程功力扎实——问题几乎全部出在「失败时怎么办」的策略层,而非机制层。