試與 UI 獎(jiǎng)勵(lì)度量框架)
jcode iOS TestHarness面向 iOS 客戶端的確定性 E2E 測(cè)試與 UI 獎(jiǎng)勵(lì)度量框架【免費(fèi)下載鏈接】jcodeThe most RAM efficient harness項(xiàng)目地址: https://gitcode.com/GitHub_Trending/jcod/jcodeios/TestHarness是 jcode 倉(cāng)庫(kù)中專(zhuān)門(mén)用于開(kāi)發(fā)和驗(yàn)證 iOS 客戶端JCodeMobile的端到端測(cè)試裝置它用一個(gè)零第三方依賴的 Python mock 網(wǎng)關(guān)在本地復(fù)現(xiàn) jcode 服務(wù)端的真實(shí)線協(xié)議把構(gòu)建應(yīng)用 → 啟動(dòng)模擬器 → 注入配對(duì)憑據(jù) → 啟動(dòng) → 截圖壓縮成一條命令并在此之上疊加了一套把這個(gè) UI 不好看量化為 0-100 可爬山hill-climb獎(jiǎng)勵(lì)的度量框架。讀完本文你將掌握如何在本機(jī)不依賴真機(jī)、網(wǎng)絡(luò)和 LLM 的情況下對(duì) jcode iOS 客戶端做可重復(fù)的協(xié)議級(jí) E2E 驗(yàn)證以及如何用ui_matrix.pyreward/聚合器對(duì) UI 改動(dòng)做回歸門(mén)控。1. 整體定位一個(gè)誠(chéng)實(shí)且可重復(fù)的本地服務(wù)端ios/TestHarness/README.md對(duì)該裝置的定位非常明確它是一個(gè)確定性deterministic、無(wú) LLM 的 harness用來(lái)替代舊版 Rust 模擬器——為客戶端提供一個(gè)誠(chéng)實(shí)、可重復(fù)的服務(wù)端行為來(lái)源讓開(kāi)發(fā)機(jī)上的構(gòu)建可以對(duì)著它工作無(wú)需真機(jī)、無(wú)需網(wǎng)絡(luò)、無(wú) provider 成本。它由三層組成協(xié)議層mock_gateway.pymock 服務(wù)端protocol_smoke_test.py純標(biāo)準(zhǔn)庫(kù)的協(xié)議斷言客戶端E2E 層run_e2e.sh一條命令跑通swift test→ 構(gòu)建應(yīng)用 → 啟動(dòng) mock → 冒煙測(cè)試 → 啟動(dòng)模擬器 → 注入憑據(jù) → 啟動(dòng)應(yīng)用 → 截圖度量層ui_metrics.py/ui_lint.py/ui_matrix.pyreward/獎(jiǎng)勵(lì)框架把 UI 質(zhì)量變成單個(gè)可爬山的數(shù)字。README 說(shuō)明其動(dòng)機(jī)JCodeKit平臺(tái)無(wú)關(guān)的客戶端核心已經(jīng)用swift test做了完整的單元測(cè)試而這層 harness 證明的是真正運(yùn)行在模擬器里的 SwiftUI 應(yīng)用能通過(guò)真實(shí)的 WebSocket 連接并渲染出真實(shí)的會(huì)話記錄。兩層合起來(lái)讓客戶端行為可以脫離真機(jī)持續(xù)改進(jìn)。2. mock_gateway.py在同一端口上說(shuō)真實(shí)線協(xié)議的 mock 服務(wù)mock_gateway.py是純標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)僅asyncio/json/hashlib/struct等無(wú)第三方依賴在單個(gè) TCP 端口上按crates/jcode-base/src/gateway.rs定義的線協(xié)議路由請(qǐng)求。它與真實(shí)網(wǎng)關(guān)的結(jié)構(gòu)對(duì)應(yīng)關(guān)系可以從 gateway.rs 看到真實(shí)網(wǎng)關(guān)用peek檢查首個(gè)數(shù)據(jù)塊中的Upgrade: websocket頭來(lái)決定走 HTTP 還是 WebSocket 分支mock 網(wǎng)關(guān)同樣通過(guò)嗅探請(qǐng)求行來(lái)路由路由行為GET /health返回{status: ok, version, gateway: true}POST /pair配對(duì)碼換 token默認(rèn)碼123456碼錯(cuò)返回401GET /wsWebSocket 升級(jí)承載換行分隔的 JSON 事件協(xié)議Authorization: Bearer token鑒權(quán)真實(shí)的/health、/pair、/ws路由在 gateway.rs 中同樣存在且真實(shí)網(wǎng)關(guān)在 WebSocket 握手階段就完成 token 校驗(yàn)token 無(wú)效會(huì)收到明確的 401——mock 的鑒權(quán)行為Bearer {token}匹配與之保持一致。2.1 命令參數(shù)mock_gateway.py 的 CLImain()中的 argparse 定義python3 mock_gateway.py [--port 7643] [--host 127.0.0.1] [--code 123456] \ [--token mocktoken0123456789abcdef] [--push-demo] \ [--scenario empty|short|tool|long|code]--port/--host監(jiān)聽(tīng)地址默認(rèn)127.0.0.1:7643--code配對(duì)碼默認(rèn)123456/pair請(qǐng)求必須攜帶相同 code 才返回 token--token配對(duì)成功簽發(fā)的 token后續(xù)/ws升級(jí)用Bearer校驗(yàn)--push-demo連接建立后主動(dòng)out-of-band推送一條notificationbuild finished和一條compaction通知用于驗(yàn)證 App 的 toast UI--scenario預(yù)置五類(lèi)確定性會(huì)話內(nèi)容scenario_messages()中定義empty空會(huì)話、short一問(wèn)一答、tool帶一次 bash 工具調(diào)用、long6 輪、含滾動(dòng)壓力、code含 Python 代碼塊。這是ui_matrix.py布局矩陣的內(nèi)容軸來(lái)源。2.2 腳本化的message響應(yīng)流mock 不調(diào)用任何 LLM收到message請(qǐng)求后stream_response()輸出一條固定事件序列覆蓋客戶端需要渲染的全部事件類(lèi)型ack→reasoning_delta× N →reasoning_done→text_delta× N6 字符一片→tool_startbash→tool_input增量 →tool_exec→tool_done輸出hello\n→text_delta× N含 代碼圍欄、粗體與行內(nèi)代碼用于驗(yàn)證 markdown 渲染→message_end→tokens累加 input/output→done。其余請(qǐng)求分支同樣全部腳本化包括subscribeack session state、get_history帶available_models、all_sessions、display_title、reasoning_effort的完整 history 載荷、soft_interrupt先回soft_interrupt_injected再流式響應(yīng)鏡像真實(shí)服務(wù)端行為、cancel、ping、set_model、set_reasoning_effort、compact、rename_session、resume_session、clear等。值得注意的是 mock 中手寫(xiě)了 WebSocket 幀編解碼encode_text_frame/read_frame/WSConn支持 126/127 擴(kuò)展長(zhǎng)度與 mask 解包并有 20 秒周期的 ping keepalive——這意味著協(xié)議保真度是逐字節(jié)的而非語(yǔ)義近似。3. protocol_smoke_test.py對(duì) happy-path 事件序列的完整斷言protocol_smoke_test.py是純標(biāo)準(zhǔn)庫(kù)的 WebSocket/HTTP 客戶端http.client 手寫(xiě) socket 幀處理可對(duì) mock 網(wǎng)關(guān)或真實(shí) jcode 網(wǎng)關(guān)運(yùn)行斷言完整 happy path。它的檢查項(xiàng)main()內(nèi)逐項(xiàng)check()可歸納為/health返回 200、status ok、gateway標(biāo)志為 true錯(cuò)誤配對(duì)碼 →401正確配對(duì)碼 → 返回 token 與server_name mock-jcodeWS 連接 subscribe→ 依次出現(xiàn)ack、session、stateget_history→history事件含非空的available_models與all_sessionsmessage→ 依次斷言ack / reasoning_delta / text_delta / tool_start / tool_exec / tool_done / message_end / tokens / done全部出現(xiàn)且流式拼接后的文本包含對(duì)輸入的回顯與 代碼圍欄set_model→model_changed事件且模型值正確history 載荷攜帶display_title與reasoning_effort會(huì)話標(biāo)題 UI 所依賴;soft_interrupt→ 斷言soft_interrupt_injected出現(xiàn)、內(nèi)容回顯、且注入確認(rèn)先于流式響應(yīng)set_reasoning_effort/compact/rename_session各自的確認(rèn)事件。任何一項(xiàng)失敗都會(huì)打印FAILED (n): [...]并以退出碼 1 結(jié)束全部通過(guò)則打印ALL CHECKS PASSED。單獨(dú)運(yùn)行協(xié)議斷言README 的用法針對(duì) mock 或真實(shí)網(wǎng)關(guān)均可python3 TestHarness/mock_gateway.py # 或運(yùn)行一個(gè)真實(shí)的 jcode 網(wǎng)關(guān) python3 TestHarness/protocol_smoke_test.py --port 76434. run_e2e.sh一條命令的完整 E2E 流水線run_e2e.sh 把整條鏈路串起來(lái)支持--device iPhone 17默認(rèn)與--push-demo兩個(gè)參數(shù)腳本開(kāi)頭cd到ios/目錄執(zhí)行。七個(gè)步驟腳本中按編號(hào)注釋swift test先跑 headless 行為層單測(cè)JCodeKit的平臺(tái)無(wú)關(guān)測(cè)試構(gòu)建xcodegen generatexcodebuild build -project JCodeMobile.xcodeproj -scheme JCodeMobile -destination platformiOS Simulator,name$DEVICE -derivedDataPath .build-ios啟動(dòng) mock 網(wǎng)關(guān)python3 TestHarness/mock_gateway.py --port 7643 --host 127.0.0.1 [--push-demo]日志落盤(pán)到$TMPDIR/jcode-ios-e2e/mockgw.log并用trap在退出時(shí)清理進(jìn)程協(xié)議冒煙測(cè)試protocol_smoke_test.py --port 7643啟動(dòng)模擬器xcrun simctl boot $DEVICE冪等全新安裝 注入配對(duì)憑據(jù)simctl uninstallsimctl install然后通過(guò)simctl get_app_container拿到數(shù)據(jù)容器直接寫(xiě)入Library/Application Support/jcode-servers.json啟動(dòng) 截圖simctl launch后等待 6 秒simctl io screenshot產(chǎn)出$TMPDIR/jcode-ios-e2e/chat.png。# 完整流水線截圖落在 $TMPDIR/jcode-ios-e2e/chat.png ./TestHarness/run_e2e.sh # 同時(shí)驗(yàn)證 out-of-band 通知 toast ./TestHarness/run_e2e.sh --push-demo4.1 自動(dòng)連接憑據(jù)是如何種進(jìn)去的步驟 6 是整個(gè)流水線里最巧妙的部分。App 把配對(duì)過(guò)的服務(wù)器保存在 Keychain當(dāng) Keychain 不可用時(shí)未簽名的模擬器構(gòu)建回退到Library/Application Support/jcode-servers.json。這一點(diǎn)在 CredentialStore.swift 中有直接證據(jù)Keychain 寫(xiě)入失敗會(huì)打印keychain write failed (%d), using file fallback并調(diào)用persistToFile()而fallbackURL正是applicationSupportDirectory下的jcode-servers.json。harness 利用的就是這條回退路徑——直接往 App 數(shù)據(jù)容器寫(xiě)一份 JSON使應(yīng)用啟動(dòng)時(shí)自動(dòng)連接從而繞開(kāi) SpringBoard 的 Open in app? 深度鏈接確認(rèn)該確認(rèn)無(wú)法腳本化。腳本寫(xiě)入的內(nèi)容run_e2e.sh[{host:127.0.0.1,port:7643,token:mocktoken0123456789abcdef, serverName:mock-jcode,serverVersion:mock-0.32.0,pairedAt:770000000}]ui_matrix.py中復(fù)用同一份憑據(jù)模板CRED常量字段一一對(duì)應(yīng)host/port指向本機(jī) mocktoken與 mock 的--token默認(rèn)值一致serverName與 mock 的SERVER_NAME mock-jcode一致。5. UI 度量三件套從單張截圖到布局矩陣這一層的設(shè)計(jì)哲學(xué)README 原話This looks ugly 被轉(zhuǎn)化為一個(gè)可爬山的單一數(shù)字。三件套分工明確——ui_metrics.py打分渲染結(jié)果像素ui_lint.py打分源碼設(shè)計(jì) token 紀(jì)律ui_matrix.py把兩者擴(kuò)展到內(nèi)容場(chǎng)景 × 設(shè)備 × Dynamic Type的矩陣。5.1 ui_metrics.py像素級(jí)評(píng)分卡ui_metrics.py對(duì)單張simctl截圖打分輸出四項(xiàng) 0-100 軸分 總分權(quán)重為0.40 * space 0.30 * consistency 0.20 * legibility 0.10 * rhythmui_metrics.pyspace內(nèi)容填充率理想 chat 填充帶約 35-65%以 45% 為峰值、垂直平衡質(zhì)心是否居中、最大死區(qū)占比consistency主色數(shù)量5-bit/通道量化后4-9 個(gè)主色為健康區(qū)間 左 margin 聚類(lèi)數(shù)紀(jì)律良好的布局應(yīng)只有 1-3 個(gè)左邊距l(xiāng)egibility最亮內(nèi)容近似文字對(duì)背景的 WCAG 對(duì)比度1:1 映射 0 分、7:1 映射 100 分rhythm內(nèi)容帶之間的垂直間隙是否貼合 8pt 網(wǎng)格。兩個(gè)值得注意的實(shí)現(xiàn)細(xì)節(jié)它先裁剪掉狀態(tài)欄5.5%與 home indicator2.5%再評(píng)分評(píng)的是 App 的內(nèi)容區(qū)不是 Apple 的時(shí)鐘設(shè)計(jì) tokenbackground 0x0F0F14、surface 0x1A1A1F、mint 0x4DD9A6等注釋標(biāo)明必須與Sources/JCodeMobile/Theme.swift保持鏡像。它是刻意渲染器無(wú)關(guān)的——只讀像素、不讀視圖樹(shù)因此同一工具可以給任意截圖打分不依賴對(duì)代碼的信任。python3 ui_metrics.py SHOT.png [--scale 3] [--json] [--annotate OUT.png] python3 ui_metrics.py --baseline a.png --candidate b.png # 對(duì)比兩張delta -0.5 時(shí)退出碼 15.2 ui_lint.py源碼級(jí)設(shè)計(jì) token 紀(jì)律ui_lint.py掃描 SwiftUI 源碼默認(rèn)根Sources/JCodeMobile檢查五類(lèi)問(wèn)題并各帶懲罰權(quán)重檢查項(xiàng)規(guī)則每次扣分raw_colorColor(red:/white:/.sRGB/hue/displayP3)字面量及Color(hex:)應(yīng)走 Theme token6raw_font.font(.system(size:))字面量應(yīng)走Theme.mono(..)4magic_radiuscornerRadius:不在{0, 8, 10, 12, 14, 16, 20, 999}刻度上2magic_pad.padding(字面量)偏離 4pt 間距網(wǎng)格{0,2,4,...,64}1long_view單個(gè)視圖文件超過(guò) 220 行預(yù)算提示拆分5Theme.swift本身豁免顏色/字體檢查token 定義處。總體分?jǐn)?shù)為100 - 總懲罰 / 文件數(shù)按文件數(shù)歸一避免新增干凈文件稀釋信號(hào)--min參數(shù)可在基線清理完后用于 CI 門(mén)控低于閾值退出碼 1默認(rèn) 0 即只報(bào)告。5.3 ui_matrix.py設(shè)備 × Dynamic Type × 場(chǎng)景 的布局矩陣單張截圖只測(cè)一個(gè)內(nèi)容狀態(tài)ui_matrix.py把矩陣軸定為設(shè)備 × Dynamic Type 尺寸 × 場(chǎng)景設(shè)備默認(rèn)iPhone 17大屏 3xiPhone SE (3rd generation)小屏 2x可用--devices覆蓋讓布局魯棒性在真實(shí)的寬高壓力的兩端都被測(cè)到Dynamic Type主設(shè)備額外以accessibility-large復(fù)跑一遍通過(guò)xcrun simctl ui dev content_size設(shè)置讓文字縮放破壞暴露在矩陣?yán)?-a11y-size 可禁用該變體場(chǎng)景默認(rèn)empty,short,tool,long,code每個(gè)場(chǎng)景對(duì)應(yīng)一個(gè)預(yù)置了相應(yīng)會(huì)話內(nèi)容的 mock 網(wǎng)關(guān)實(shí)例即第 2.1 節(jié)的--scenario。每個(gè)單元cell的流程殺掉舊 mock → 以該場(chǎng)景啟動(dòng) mock 網(wǎng)關(guān) →simctl uninstall install保證每次都是真·冷啟動(dòng)→ 寫(xiě)入憑據(jù) →simctl launch→ 截圖 → 交給ui_metrics.analyze()打分 → 結(jié)束前把設(shè)備 Dynamic Type 恢復(fù)默認(rèn)值絕不把模擬器留在 accessibility 尺寸上。運(yùn)行時(shí)性能指標(biāo)README 與 ui_matrix.py 一致每個(gè)單元在 schema 上記錄盡力而為best-effort的兩項(xiàng)指標(biāo)供reward/scorers/perf.py消費(fèi)cold_launch_ms全新安裝后simctl launch的墻鐘耗時(shí)進(jìn)程拉起并返回 pid是冷啟動(dòng)到進(jìn)程就緒最廉價(jià)的近似first_frame_ms輪詢截圖直到屏幕上約 30% 像素接近 App 背景色0x0F0F14與 Theme 一致為止的耗時(shí)超時(shí) 12 秒。README 對(duì)此有明確的誠(chéng)實(shí)性限定測(cè)量包含 harness 自身開(kāi)銷(xiāo)應(yīng)視為一致的相對(duì)信號(hào)而非絕對(duì)真值若測(cè)量失敗單元直接省略runtime字段perf 評(píng)分器降級(jí)為不可用權(quán)重重新歸一缺失數(shù)據(jù)永遠(yuǎn)不會(huì)拉垮獎(jiǎng)勵(lì)。--no-perf可整體跳過(guò)滾動(dòng)卡頓采集尚未實(shí)現(xiàn)scroll_jank_frac保持缺省。矩陣跑完后輸出聚合表含MEAN overall與WORST cell均值就是要爬的那座山一個(gè)布局改動(dòng)只有在整個(gè)矩陣的均值上提升才算改進(jìn)而不是某一張幸運(yùn)截圖上的提升。python3 ui_matrix.py [--devices iPhone 17,iPhone SE (3rd generation)] \ [--scenarios empty,short,tool,long,code] [--a11y-size accessibility-large] \ [--no-perf] [--out DIR] [--json] # 回歸門(mén)控候選均值不低于基線 0.5 分以內(nèi)才通過(guò) python3 ui_matrix.py --baseline-json before.json --candidate-json after.json6. reward/完整的 UX 獎(jiǎng)勵(lì)框架reward/目錄把上面的度量聚合為一個(gè) 0-100 的單一獎(jiǎng)勵(lì)。reward/REWARD_SPEC.md給出生成管線截圖(s) 源碼樹(shù) (可選) AX 樹(shù) / 運(yùn)行時(shí) traces - 每個(gè)類(lèi)別獨(dú)立的 scorer - CategoryScore(0..100, evidence) - 加權(quán)和歸一 - 總體獎(jiǎng)勵(lì) (0..100) 分類(lèi)別分解 最差單元標(biāo)注一切在本機(jī) headless 運(yùn)行mock 場(chǎng)景 simctl截圖無(wú) LLM、無(wú)真機(jī)、無(wú)網(wǎng)絡(luò)。6.1 類(lèi)別與權(quán)重REWARD_SPEC 的核心原則是以真實(shí)用戶每分鐘的感知優(yōu)先1長(zhǎng)時(shí)間閱讀流式文字與工具輸出 → 可讀性優(yōu)先2高頻流程發(fā)送、打斷、切會(huì)話的點(diǎn)擊成本 → 人機(jī)工效次之抽象的像素幾何美學(xué)重要但不能蓋過(guò)讀得清、夠得著。當(dāng)前規(guī)范的六個(gè)類(lèi)別權(quán)重合計(jì) 1.0A. Space density (0.15)沒(méi)有浪費(fèi)的像素——space_efficiency(0.05)、information_density(0.05)、content_safety(0.05無(wú)裁切/溢出/截?cái)?前兩者對(duì)場(chǎng)景感知empty場(chǎng)景按安靜的空態(tài) 可見(jiàn)的起始入口評(píng)分而不是按 30-60% 的會(huì)話填充帶評(píng)分。B. Ergonomics interaction (0.30)權(quán)重最高因?yàn)橛脩裘繒?huì)話要觸摸這個(gè) App 幾十次——touch_targets(0.10 44×44pt)、reachability(0.08拇指舒適區(qū))、interaction_cost(0.12基于 KLM/Fitts 模型、以真實(shí)使用日志為據(jù)的每動(dòng)作期望秒數(shù))。C. Visual clarity (0.12)visual_hierarchy(0.04)、consistency(0.04)、rhythm(0.048pt 網(wǎng)格)。D. Legibility accessibility (0.22)對(duì)這類(lèi)產(chǎn)品杠桿最高的類(lèi)別——contrast(0.14真實(shí)渲染內(nèi)容的 WCAG 對(duì)比度含暗色二級(jí)/工具輸出層)、accessibility(0.08VoiceOver、Dynamic Type、reduce-motion、語(yǔ)義角色)。E. Responsiveness (0.09)layout_robustness(0.05矩陣內(nèi)方差懲罰脆弱布局)、perf(0.04冷啟動(dòng)到首幀 滾動(dòng)流暢度best-effort可降級(jí) N/A)。F. Design authenticity craft (0.12)設(shè)計(jì)過(guò)而非生成出來(lái)的——styling、simplicity、ai_patterns反AI 味懲罰紫/靛/青 slop 配色、漸變文字、玻璃擬態(tài)濫用、通用字體等0 個(gè) tell 即 100 分依據(jù)reward/AI_SLOP_RESEARCH.md。這里有一處值得指出的倉(cāng)庫(kù)內(nèi)部不一致ios/TestHarness/README.md仍描述為13 個(gè)評(píng)分器跨 5 個(gè)加權(quán)類(lèi)別A .30 / B .25 / C .20 / D .15 / E .10而倉(cāng)庫(kù)當(dāng)前reward/scorers/下實(shí)際有 16 個(gè)評(píng)分器模塊REWARD_SPEC.md定義的也是上表 6 類(lèi)別的新權(quán)重分布。從兩者對(duì)照看README 記錄的是框架的較早快照而REWARD_SPEC.md與評(píng)分器目錄是當(dāng)前的權(quán)威狀態(tài)實(shí)際運(yùn)行時(shí)權(quán)重以各 scorer 模塊的WEIGHT常量為準(zhǔn)聚合器讀取模塊屬性而非硬編碼類(lèi)別表。6.2 評(píng)分器契約與聚合每個(gè) scorer 是reward/scorers/name.py下的一個(gè) Python 模塊暴露固定契約NAME space_efficiency # 唯一 id對(duì)應(yīng)分類(lèi)法 CATEGORY A # 分類(lèi)法組字母 WEIGHT 0.12 # 框架內(nèi)相對(duì)權(quán)重 def score(ctx: Context) - CategoryScore: 純函數(shù)讀 ctx返回 CategoryScore。無(wú)全局可變狀態(tài)。Contextreward/context.py提供評(píng)分器可能需要的全部輸入截圖路徑 解碼后的 numpy 數(shù)組、設(shè)備與場(chǎng)景、px-per-point scale、源碼根目錄、可選 AX 樹(shù) JSON、可選運(yùn)行時(shí)指標(biāo)字典CategoryScorereward/types.py為{name, category, weight, value: 0..100, evidence: dict, available: bool}availableFalse表示此處測(cè)不了例如沒(méi)有 Instruments 時(shí)的 perf聚合器會(huì)丟棄并重新歸一權(quán)重缺失數(shù)據(jù)不會(huì)悄悄拉垮獎(jiǎng)勵(lì)場(chǎng)景感知通過(guò)ctx.scenario實(shí)現(xiàn)例如empty是刻意設(shè)計(jì)的空態(tài)場(chǎng)景感知型評(píng)分器應(yīng)在 evidence 里報(bào)告mode鍵便于逐單元審計(jì)評(píng)分器必須確定、無(wú)副作用由reward/test_determinism.py強(qiáng)制同輸入 → 同輸出。聚合器reward/aggregate.py的行為main()與aggregate()discover_scorers()通過(guò)pkgutil.iter_modules自動(dòng)發(fā)現(xiàn)scorers/下滿足契約的模塊缺失契約屬性的模塊發(fā)警告并跳過(guò)逐單元加權(quán)平均僅 available 的評(píng)分器權(quán)重歸一單個(gè)評(píng)分器拋異常會(huì)被捕獲并降級(jí)為availableFalse不會(huì)殺死整輪運(yùn)行總體 跨單元均值同時(shí)給出最差單元device/size/scenario與最差類(lèi)別——告訴你下一步該修什么支持--baseline/--candidate回歸門(mén)控delta -0.5 則退出碼 0否則 1可直接進(jìn) CI并行安全是顯式設(shè)計(jì)目標(biāo)一評(píng)分器一文件意味著 swarm 工作流中的多個(gè) worker 永遠(yuǎn)不會(huì)編輯同一文件契約 types.py/context.py是唯一共享 API誠(chéng)實(shí)性則是評(píng)分器讀渲染出的像素與真實(shí)源碼而非 view model無(wú)法通過(guò)謊報(bào)狀態(tài)來(lái)作弊。6.3 典型爬山循環(huán)README 原樣流程# 1. 采集截圖矩陣并打分 python3 ui_matrix.py --json /tmp/before.json python3 -m reward.aggregate --matrix-json /tmp/before.json --out-json /tmp/before_reward.json # 2. 做一處 UI 改動(dòng)重新構(gòu)建重新測(cè)量 python3 ui_matrix.py --json /tmp/after.json python3 -m reward.aggregate --matrix-json /tmp/after.json --out-json /tmp/after_reward.json # 3. 門(mén)控僅當(dāng)獎(jiǎng)勵(lì)未回退時(shí)才保留改動(dòng) python3 -m reward.aggregate --baseline /tmp/before_reward.json --candidate /tmp/after_reward.json # 評(píng)分器必須保持純函數(shù)/確定性 python3 -m reward.test_determinismaggregate.py也支持對(duì)單張截圖打分--shot a.png --device iPhone 17 --scenario short --scale 3--source-root默認(rèn)指向ios/Sources/JCodeMobile。6.4 擴(kuò)展方式一文件即插即用新增一個(gè)類(lèi)別是往reward/scorers/丟一個(gè)文件的事滿足契約NAME、CATEGORY、WEIGHT、score(ctx) - CategoryScore即可被聚合器自動(dòng)發(fā)現(xiàn)。README 與 REWARD_SPEC 對(duì)此的表述一致aggregate.py的發(fā)現(xiàn)邏輯檢查四個(gè)契約屬性齊備也印證了這一點(diǎn)——沒(méi)有任何注冊(cè)表需要維護(hù)。7. 適用前提與運(yùn)行環(huán)境平臺(tái)E2E 流水線與 UI 矩陣依賴xcodegen、xcodebuild、xcrun simctl只能在 macOS Xcode 環(huán)境運(yùn)行mock_gateway.py與protocol_smoke_test.py為純 Python 標(biāo)準(zhǔn)庫(kù)可在任意平臺(tái)獨(dú)立運(yùn)行對(duì) mock 或真實(shí)網(wǎng)關(guān)做協(xié)議斷言依賴ui_metrics.py/ui_matrix.py的像素分析需要numpy與Pillowui_matrix.py中背景占比檢測(cè)對(duì)兩者做了try/except保護(hù)缺失時(shí)降級(jí)為 0.0端口與設(shè)備默認(rèn)端口7643、默認(rèn)設(shè)備iPhone 17、Bundle IDcom.jcode.mobile均可通過(guò)參數(shù)覆蓋模擬器限制憑據(jù)注入依賴 Keychain 不可用時(shí)的文件回退路徑未簽名模擬器構(gòu)建這是該機(jī)制成立的前提誠(chéng)實(shí)性邊界cold_launch_ms/first_frame_ms含 harness 開(kāi)銷(xiāo)是相對(duì)信號(hào)perf 類(lèi)評(píng)分缺失時(shí)降級(jí)而非記零。8. 小結(jié)ios/TestHarness的價(jià)值在于把三件通常彼此分離的事焊成了一條確定性流水線線協(xié)議級(jí) mock 服務(wù)端逐字節(jié)對(duì)齊crates/jcode-base/src/gateway.rs的/health、/pair、/ws協(xié)議、一條命令的模擬器 E2E利用jcode-servers.json文件回退路徑注入憑據(jù)繞開(kāi)不可腳本化的系統(tǒng)確認(rèn)彈窗、以及可門(mén)控的 UI 獎(jiǎng)勵(lì)度量像素評(píng)分 源碼 lint 矩陣均值 16 個(gè)可插拔評(píng)分器聚合為 0-100 獎(jiǎng)勵(lì)--baseline/--candidate讓每次改動(dòng)都有可測(cè)的 /- delta。對(duì)任何想在不碰真機(jī)的前提下讓移動(dòng)客戶端行為可爬山的團(tuán)隊(duì)這套mock 網(wǎng)關(guān) 場(chǎng)景矩陣 獎(jiǎng)勵(lì)聚合的組合本身就是一個(gè)可參考的工程范式。【免費(fèi)下載鏈接】jcodeThe most RAM efficient harness項(xiàng)目地址: https://gitcode.com/GitHub_Trending/jcod/jcode創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考