GUITaboo Async Throughput Root-Cause Audit
State 版本、Lineage 与审核结果复用审查
冻结运行产物是 deployment/ape-rn-routing.jar,SHA-256 为
33c71e466eefb86177f43fdd63a070e4d1494cee79ab976cc752d1fce7f8fd54。当前工作区仍有未提交修改,以下代码位置按正式构建对应的源码与
deployment/native-source-sha256.json 审查,不能把当前 dirty
HEAD 当成运行版本。
实际演化机制
Model.rebuild()(native/src/com/android/commands/monkey/ape/model/Model.java:162-239)扫描命名变化,移除受影响 State/Transition,递增version,调用graph.setVersion(version),再从受影响 GUI 重建 State/Transition。Graph.setVersion()(Graph.java:199-201)只改 Graph 前缀;新 State 在getOrCreateState()(Graph.java:247-270)按g<version>s<counter>生成新的对象和 ID。旧 State 可能从 Graph 删除。StateNamingManager.updateNaming()(StateNamingManager.java:89-177)保存 Naming parent-child 的 refinement/abstraction 边,但没有把旧 State ID 映射到新 State ID 的持久 lineage 表。Model.update()只在当前对象被判定为 stale 时,按当前 GUI/XPath 重新找 State/Action。JevRegistry.reset()(JevRegistry.java:251-261)把审计 Entry 的 version 改为当前值,却清空 native executionIndex/relatedIndex;没有生成旧 State 到新 State 的 lineage 映射。confirmedHistory()还要求 confirmation model/version/state identity 完整一致(JevRegistry.java:315-340)。
因此当前系统有 Naming refinement 历史和 GUI/Transition 重建依据,但没有可供 Ledger 直接查询的“旧 State → 新 State”确定关系。State ID 相同也不能被假定为对象延续;同一 State object 也不代表当前 GUI target 仍可执行。
审核保存与页面执行是两层条件
JevSemanticReview.Cache 保存
sourceGui、sourceTree、sourceState、sourceVersion、historyRevision、confirmationCount
和 request/input
fingerprint(JevSemanticReview.java:50-76, 329-335)。
- 到达归档
archiveOnArrival()(577-600)只保存原始 HTTP/模型文本,明确写入validated=false, applied=false;它没有进入 Ledger。 - 同步/轮询消费
consumeCache()(840-848)先要求sourceVersion == modelVersion、historyRevision == executionRevision(source action)、confirmationCount == registry.confirmationCount();任一不等就写STALE_HISTORY_OR_VERSION并删除 pages/inFlight。 - 分页回访
resumeRetained()(603-651)还要求old.sourceState == state(对象身份)、同版本、同相关 Action execution revision、同 confirmation count;当前 GUI identity 与原sourceGui不同则只continue,结果保留但此轮不消费(626-630)。只有相同 GUI identity 才调用consumeCache()。 - 通过消费后,Pairwise 聚合和 Ledger update
才发生(
855-917)。Candidate 当前是否仍未精确执行是另一个检查;current失败会使结果成为STALE_RESULT,但不是“模型结果无法解析”。 - 已接受的
PROBE在JevReviewLedger.staged()对 version/revision 有放宽,但仍要求sourceStates.get(key) == state、Action type/target 一致(JevReviewLedger.java:230-251);JevSemanticReview.executable()还要求当前 node/action enabled、对应节点重定位成功、navigation mask 允许(422-431)。
结论:模型判断可以合法进入 Ledger 的条件与当前 GUI 能立即执行 Probe 的条件本来就不同;现有实现却在“消费前”把 State object/version/history/confirmation 作为硬门槛,导致很多本可保存的判断在 Ledger 之前被丢弃。
778 条 stale 能确认到什么
原始归档只把终态写成单一
STALE_HISTORY_OR_VERSION,没有写触发的是哪一个 OR
分支。逐条 async-loss-analysis.csv 可确认:
| 终态 | 数量 | 能从归档确认的事实 |
|---|---|---|
| StartLux stale | 607 | 响应归档/消费条件失败;其中原始模型文本仍保存在 response/arrival 归档,无法由终态区分具体 OR 分支 |
| Jev stale | 171 | 同上;另有 67 条 Jev 是 HTTP 200 到达但没有消费,未归入 stale |
| 合计 stale | 778 | 只能精确报告复合条件,不应伪拆 |
代码分支的安全细分是:sourceVersion != modelVersion、sourceState != state、historyRevision != executionRevision(...)、confirmationCount != registry.confirmationCount()、first == null
或当前 exact execution 变化。归档没有记录消费瞬间的当前
version、revision、confirmation count、State object 身份和 exact-match
结果,所以这些细分必须保留 UNKNOWN。对 response receipt
可观察到少量“消费时已有更高
abstraction_version”的实例,但这只是版本变化的下界,不能证明其余 stale
都不是版本触发。
没有证据证明“GUI Tree 变化”本身使这 778 条全部失效:GUI 不同在
resumeRetained() 中首先造成暂缓回访,而不是立即标
stale;真正 stale
发生在随后硬条件检查。也没有证据证明所有历史增长都与候选相关,因为实现使用的是单个来源
ModelAction 的 execution revision,同时还检查全局 confirmation
count。
安全复用条件(建议,不在本轮实现)
可把复用拆成四个独立闸门:
- Lineage:从已有 GUI/Transition/Naming 重建证据确认旧 State 与当前 State 是同一有效演化链;现有代码没有现成持久关系,不能只比较 State ID。
- Concrete
continuity:
ConcreteActionIdentity(actionType, canonicalFullName)在当前候选中唯一重定位,且没有同身份歧义、已确认执行或 enabled 变化。 - Witness validity:原始 input fingerprint、selected witness IDs、Pairwise evidence view 和相关 ModelAction execution revision 未改变;无关 State 的执行不应使其失效。
- Execution binding:保存 Ledger/Pending Probe
可以不要求当前页面立即相同;真正执行前再做
correspondingNode、enabled、action validity、navigation mask 和 native validator 检查。
只有四项都通过才能把原结果保存为 Ledger
PROBE/REPRESENTED;第 4 项失败时保留判断但不执行。无法建立
lineage 或 Concrete 唯一对应时保持 stale。
这比放宽 Concrete 身份安全,因为它只减少无关 version/global-history 造成的误杀,不把不同操作合并,也不直接执行旧 Probe。