# STEP-114 独立 Fast Smoke 场景与 RDC 验收 v1 日期:2026-10-11。状态:**源码交付;下列编译、测试和设备操作全部 NOT RUN。** Codex 不执行本页命令。Mac Native 由 RDC/普通 Terminal 独立执行;Android 构建、安装、ADB 与实体机步骤由用户在后续授权条件下手工统一执行。 ## 1. 场景入口和范围 Test App:`STEP 093 → STEP-114 独立 FAST_SMOKE(非正式 PBF)`。正式网络 PBF 仍在 `STEP 093 → STEP-113 FZG1-PBF GATE`,保留 PBF63、STATIC_RC、PARTICLE_OFF、PARTIAL_RCRR、SOURCE_CHANGE 原实现。 | 独立场景 | 预期层序/保护 | 待验收结果 | |---|---|---| | FAST_SMOKE | R=background + 非空本地 GeoJSON/circle;C=WeatherColor + RasterParticle。无 Annotation 实例时删除空内建 Annotation layer,保留 source | Native 实际4层;R Build/Reuse 和 Full/Fast 必须真实增长 | | CIRCLE_C_BLOCKED | baseline 后追加普通 circle C;Annotation 层仍按独立空场景移除 | 实际5层,#22/#23 阻止 Fast;R 可正常 Build/Reuse,不等于整个 scene 可 Fast | | ANNOTATION_BLOCKED | baseline 加正常自动内建 Annotation Symbol C,并创建真实 marker;不删该层 | 实际5层;#22/#23 阻止 Fast;marker 可见、可单次 position 更新 | | SYMBOL_R_BLOCKED | baseline 的 R 内加本地16×16图标 Symbol;无需网络 glyph/sprite | 实际5层;Symbol R 首建仍被 Placement gate 阻止;不能把关闭 transitions 当修复 | 全部使用既有离线 ECMWF canonical 双帧资产、固定预报 `1786060800000` ms、PAUSE、30fps目标和1024粒子,SDK camera 初始33.45/129.97、zoom10.5、bearing/pitch0;手势禁用且退出恢复。30fps目标不是实际帧率测量。actualForecastTimeObservedMs 保持 null,不能当成 Native 已回读请求时间。sceneReady 只表示加载回调与控制命令成功,不替代 Native 层表/valid/实际渲染核验。 不要在旧正式 V23 APK 上验收本轮新入口;版本号未变,必须保存本轮源码 diff/hash、build-info 的 dirty 状态与 APK SHA。不得发行或覆盖已有版本。 ## 2. Mac Native 精确定向命令(仅 RDC 执行) 基于现有 `macos-metal` preset 和实际 `mbgl-test-runner` target,无全仓测试、无 Jest。STEP-113 曾缺 libuv;本轮没有安装依赖。配置失败则停止,不运行旧 runner。 ```bash cd /Users/tei/dev/MapLibreParticleLab bash <<'SH' set -eu mkdir -p artifacts/maplibre-particle/step114-native if cmake --preset macos-metal > artifacts/maplibre-particle/step114-native/configure.log 2>&1; then echo 'configure exit=0' else step114_rc=$? tail -n 80 artifacts/maplibre-particle/step114-native/configure.log exit "$step114_rc" fi if cmake --build build-macos-metal --target mbgl-test-runner --parallel 3 > artifacts/maplibre-particle/step114-native/build.log 2>&1; then echo 'build exit=0' else step114_rc=$? tail -n 80 artifacts/maplibre-particle/step114-native/build.log exit "$step114_rc" fi if build-macos-metal/mbgl-test-runner --gtest_filter='Step114FastPathSafety.*:FastGateTrace.*:ParticleFastPath.*:RetainedUnderlayCache.*:RetainedUnderlayRevision.*:RenderSegmentPlacementFree.*:RenderSegmentLocality.*:RenderSegmentPlan.*:RenderSegmentDiagnostics.*:RenderOrchestratorError.*:RenderOrchestratorTeardown.*:ImageManager.*:GlyphPBF.*' --gtest_color=no --gtest_output=xml:artifacts/maplibre-particle/step114-native/tests.xml > artifacts/maplibre-particle/step114-native/tests.log 2>&1; then tail -n 20 artifacts/maplibre-particle/step114-native/tests.log else step114_rc=$? tail -n 80 artifacts/maplibre-particle/step114-native/tests.log exit "$step114_rc" fi SH ``` 记录 configure/build/tests 各退出码、耗时、实际测试数量与是否 skipped;失败回传首个真实 error、target、必要上下文与日志路径。新增测试应实际发现3个 `Step114FastPathSafety` 用例,否则不能把空过滤器执行成功作为 PASS。这些主要是纯状态机/结构判定测试,**不能证明 Android GPU Fast 命中、帧像素一致或 iOS 设备通过**。 ## 3. Android 后续一次集中手工验收 由用户手工,在设备 ABI=x86_64 且 Java17/SDK 环境满足现有规范时,运行唯一既有 Validation Pipeline;不自行改 ABI、build 参数或依赖,不全量 clean。 ```bash cd /Users/tei/dev/MapLibreParticleLab ./tools/particle_android_validation/build_validation_apk.sh ``` 失败保留 build.log,禁止复用旧 APK。用户手工核对同包签名和数据后再安装;本页不授权卸载用户数据。下面 ADB 全部固定既有 FZG1-2 serial,仅作为用户后续手工命令: ```bash adb -s 192.168.200.139:5555 shell getprop ro.product.cpu.abi adb -s 192.168.200.139:5555 install -r artifacts/maplibre-particle/android-validation/MapLibreAndroidTestApp-validation-opengl-debug-x86_64.apk adb -s 192.168.200.139:5555 shell am start -S -n org.maplibre.android.testapp/org.maplibre.android.testapp.activity.particle.ParticleCoreLayerValidationActivity --es step114_scenario FAST_SMOKE ``` 启动后预热10秒,再观察60秒。在开始和结束分别点击“导出当前 Native 快照”。标签 `STEP114Gate` 每30秒只输出真实 Native 摘要,完整JSON写 App 私有 files。生命周期计数可以含预热,必须比较同 lifecycle/sceneGeneration 的两次计数 delta;不要称整个累计为60秒帧率。 菜单“单次更新 R GeoJSON”“Pan/Zoom 往返”“单次 Image 变更”“禁用/恢复缓存”保存 before/submitted/100/500/1500ms 的5份快照。一次只做一项,等恢复稳定并手动导出后再做下一项。自动文件有 actionSequence 和 phase,不追加 candidate 输入或人工 frame 结果。 每个负场景单独启动、预热、观察、导出,不把以下命令连续执行当验收: ```bash adb -s 192.168.200.139:5555 shell am start -S -n org.maplibre.android.testapp/org.maplibre.android.testapp.activity.particle.ParticleCoreLayerValidationActivity --es step114_scenario CIRCLE_C_BLOCKED adb -s 192.168.200.139:5555 shell am start -S -n org.maplibre.android.testapp/org.maplibre.android.testapp.activity.particle.ParticleCoreLayerValidationActivity --es step114_scenario ANNOTATION_BLOCKED adb -s 192.168.200.139:5555 shell am start -S -n org.maplibre.android.testapp/org.maplibre.android.testapp.activity.particle.ParticleCoreLayerValidationActivity --es step114_scenario SYMBOL_R_BLOCKED ``` ANNOTATION_BLOCKED 点击“单次更新真实 Annotation”,截图应见位置变化,Native 保持 fail-closed。FAST_SMOKE 中点击该操作会明确 BLOCKED(没有 marker),不创建隐藏 Annotation 或假事件。 ## 4. 回读完整快照(用户手工) 在普通 bash Terminal 保存全部 step114 JSON,不通过 Logcat 分块拼接。路径来自当前 Debug App: ```bash cd /Users/tei/dev/MapLibreParticleLab bash <<'SH' set -eu mkdir -p artifacts/maplibre-particle/android-validation/step114 adb -s 192.168.200.139:5555 shell run-as org.maplibre.android.testapp ls files > /tmp/step114-files.txt tr -d '\r' < /tmp/step114-files.txt > /tmp/step114-files-normalized.txt while IFS= read -r step114_file; do case "$step114_file" in step114-*.json) adb -s 192.168.200.139:5555 exec-out run-as org.maplibre.android.testapp cat "files/$step114_file" > "artifacts/maplibre-particle/android-validation/step114/$step114_file" ;; esac done < /tmp/step114-files-normalized.txt SH adb -s 192.168.200.139:5555 logcat -d -v time -s STEP114Gate:I '*:S' > artifacts/maplibre-particle/android-validation/step114/device.log adb -s 192.168.200.139:5555 logcat -b crash -d > artifacts/maplibre-particle/android-validation/step114/crash.log adb -s 192.168.200.139:5555 exec-out screencap -p > artifacts/maplibre-particle/android-validation/step114/screen.png ``` 每阶段独立命名截图/日志;同名覆盖前先归档。App files 自动快照只在显式操作和生命周期变化时创建,数量无硬上限;验收后由用户管理归档,Codex 不删文件。100ms采样也可能错过一帧的变更窗口,未捕获不能标反向 Gate PASS,需保留日志再复核。 下面是读取**实际导出文件**的正向检查;把路径替换为稳定 FAST_SMOKE 手动快照的明确文件名(不要随意用最后的 teardown/旧场景文件): ```bash python3 - 'artifacts/maplibre-particle/android-validation/step114/替换为稳定FAST_SMOKE文件.json' <<'PY' import json, sys from pathlib import Path d = json.loads(Path(sys.argv[1]).read_text()) assert d['scenario'] == 'FAST_SMOKE' and d['sceneReady'] and d['blockedReason'] is None n = d['native']; t = n['fastGateTrace'] assert n['valid'] and n['styleLayerCount'] == 4 assert [l['layerType'] for l in n['styleLayers']] == ['background', 'circle', 'weather-color', 'raster-particle'] assert [s['strategy'] for s in n['segments']] == ['RETAINED', 'CONTINUOUS'] r, c = n['segments'] assert (r['beginIndex'], r['endExclusive'], c['beginIndex'], c['endExclusive']) == (0, 2, 2, 4) assert not r['placementCapable'] and r['placementGatePassed'] assert r['cache']['sourceReady'] and r['cache']['sourceStable'] and r['cache']['supported'] assert r['cache']['build'] > 0 and r['cache']['reuse'] > 0 assert t['fullRendered'] > 0 and t['fastRendered'] > 0 and t['decisionMismatch'] == 0 assert len(t['gates']) == 24 fs = [f for f in t['frames'] if f['completed'] and f['outcome'] == 'fast-rendered' and f['post']['configRevision'] == n['configRevision'] and f['post']['topologyRevision'] == n['topologyRevision'] and f['post']['contentRevision'] == n['contentRevision']] assert fs, '完整窗口没有真实完成的 Fast 帧' for f in fs: assert f['candidate'] and f['decisionConsistent'] and f['knownMask'] == 0xffffff and f['failMask'] == 0 assert f['gateStatus'] == ['PASS'] * 24 assert not f['cacheInputsTruncated'] and f['cacheInputs'] assert all(x['valid'] and x['buildRevision'] == x['staticRevision'] for x in f['cacheInputs']) assert f['post']['fullCpuFrame'] == f['pre']['fullCpuFrame'] assert any(x['actualExecution'] == 'composite' for x in f['post']['retained']) print('仅此快照 Native 正向证据通过;反向/生命周期/视觉/双平台仍需独立验收') PY ``` 不能通过修改导出的 JSON、补造 frame、重命名旧文件或放宽断言制造结果。 ## 5. 定向反向、生命周期与回归矩阵 | 项目 | 必需证据/接受条件 | |---|---| | 稳定 baseline | 两次同生命周期快照 Fast/Reuse delta>0;至少一个 completed Fast 的24 Gate全通过;Build>0;orange R circle可见,天气/粒子正常;decisionMismatch=0 | | Source mutation/异步 Tile | 单次sourceMutationCount增长;sources 表及 lastMutation 定位 step114-r-geojson;content/source revision变化;#8/#5/ready/pending 等真实拒绝证据、成功布局后 Full/Build 增长再恢复 Fast。tombstone/失败布局用Native状态机用例补充,不冒充GPU验证 | | Image 更新 | images signature/content/global revision 导致 Full;重建后恢复。baseline图标未引用也保守失效;不能宣称已验证 glyph atlas像素 | | Glyph | baseline完全无文字;本轮未新增 glyph mutation Test App控制。已有 GlyphPBF/ImageManager 测试仅资源回归;真实 Symbol glyph URL/font/atlas变化属于 Symbol结果证书阻塞,NOT VERIFIED,不能以image替代 | | Pan/Zoom 往返 | SDK cameraObservedFromSdk与Native gate/key对应,Full/Build增量;恢复相机后不能直接复用旧key;截图无陈旧circle/错位 | | 缓存禁用/恢复 | 禁用时#2失败、实际Full/live,无completed Fast增长;恢复后合法Build与Fast恢复;比较截图确认R内容/层序一致 | | Reload | 保存reload-before与加载后快照;sceneGeneration变更、Native当前层表/plan正确,重新Build;trace lifecycle仅在实际Native reset时变,不要求UI generation=Native lifecycle | | 非粒子C/真实Annotation | CIRCLE_C_BLOCKED/ANNOTATION_BLOCKED稳定窗口#22/#23失败、无completed Fast;真实marker可见/位置变化;#24是短暂pending保护,不要求在3秒末态仍FAIL | | Symbol R | 本地icon可见;R placementCapable=true,正常Full placementGatePassed=false/Build0或对应安全fallback;Fast不放行,scene #23可能另挡;无证书不改判定 | | 前后台 | baseline导出、手工Home后台10秒、回前台观察/导出;pause-before文件、恢复后Native增长、无死循环/错层/crash。此处未强制改变WeatherRuntime播放语义 | | Teardown/重建 | 退出场景/关闭Activity产生teardown-before,dispose与callback取消;用户确认MapView销毁返回/无crash,重新启动baseline。teardown-before不是销毁完成证明 | | 原PBF5场景 | 分别采集STEP-113五场景;仍保留全部基础63层定义/顺序及Annotation;缺tide时应明确missing-source,CustomMVT若实际出现仍unknown;不要求生产Fast>0、不篡改旧统计 | | 原STEP-102 | T1/T2/T3/T4 fixture和既有动态poll回归;尤其含Symbol路径不因本轮无Symbol优化被放行 | | iOS/Metal | 同Shared Core源码的Mac Metal定向测试先执行;iOS实机与GPU像素/性能未验收。Android Fast成功不能替代此项 | ## 6. 收口规则 记录各场景来源commit/dirty diff、APK SHA/版本、device serial/ABI、真实Native层表/plan、before/after文件与SHA、24Gate、Full/Fast/Build/Reuse增量、截图、首错及crash日志。processSimulationAdvance是进程级,不作为单Map Fast证据。post的原始 placementTransitionActive仍可能true,但无Symbol的preFastPlacementTransition=false是本轮受控变化,decisionMismatch应仍0。 没有真实Fast或反向证据不足,保持 PARTIAL;Symbol证书/CustomMVT/C放行分别继续 BLOCKED/fail-closed。不得根据静态检查、runner退出0或APK成功宣称整个Fast Path修复/性能PASS。Codex未commit/push,RDC后续Git收尾另需明确授权与scoped路径,不包括STEP-110/111或正式发行。