feat(app): unify external session lists with CLI working lamp - #71
Closed
Florious95 wants to merge 861 commits into
Closed
feat(app): unify external session lists with CLI working lamp#71Florious95 wants to merge 861 commits into
Florious95 wants to merge 861 commits into
Conversation
…app+t.rv.instr 复位并把判者的三条 rework 理由写进 t.app 派单正文 现象:t.rv.instr 判 rework,route_hop 已记(audit.route_hops 第2跳),源格转 blocked['await_reentry:t.app'], 但 t.app 仍是 succeeded ⇒ 前沿为空 ⇒ 驱动器收工 AwaitingHuman(idle_reason=no_dispatch)。 --drive --once 复跑同样 rc=3、写回为空 ⇒ 不是一次性抖动,是回环第二轮压根点不着火。 处置(不停工):复位两格 state=planned、清 status_record/blocking_reasons,revision 5→7; attempts 与 route_hops 全部保留(轮次上限 3 仍然生效,不会无限返修)。 判者裁定书从 wt-pb-rv1 抄进 main 留证。
指纹 = worktree HEAD + 未提交改动清单哈希。账本 state 两轮都是 succeeded, 只有指纹能分出「这是返修后的新交付」——不修的话第二轮的修复会悄悄不进 PR。
…者自己的 worktree ① 引擎在判者路由去下游时会「拒关成功」(route_contradicts_success) 把判者格标 blocked, 拿 state==succeeded 当门禁会让已 pass 的链子永远不 land ⇒ 改看账本记的 status_record.status。 ② 裁定书住在判者的 worktree 里;先找仓根会读到旧抄本(实撞:读到上一轮的 rework,卡死)。 两处不一致时 ⛔ 不放行并记一笔——说不清以哪个为准的东西不许并线。 实跑自证:corral-core#7 与 corral-serve#3 已 MERGED(判据绿→封版→PR→判者 pass→并线→推 main)。 t.red/PR#6 撞 add/add 冲突已 park 未解,原因是我账本让先红格与转绿格各起一棵树,记进 escalations。
pb-emu 交了一份诚实的判不出:30/30 冷点开里 first_frame_recv 与 snapshot_applied 一次没出现、
first_draw 恒 emitted=0 reason=glyphs_zero,而服务端旁证说推了快照
(snapshots_from_subscribe=32、perf_subscribe done-recv 60-80ms)。
⛔ 它没编数、没拿 partial_stats 冒充基线、没换取数方式——这份判不出比一份假基线值钱。
它同时指出仪表自己的盲区:两个打点都在静默 return 后面(onBinary 的 ref 守卫、opens[ref] ?: return),
「帧没到」与「帧到了被守卫吞了」在日志里都是沉默。任务一的验收判据原话是
「光看日志就能判断该做而没做还是做了但做错了」——现在做不到,说明判者上一轮 pass 给早了。
处置(回炉纪律:不许在盲的状态下修):
- 新格 t.instr2 只补仪表:两条静默路径各补 emitted=0 留痕,ref 不匹配必须同时给 frame_ref= 与 want_ref=;
判据 judge-instr2.sh 只判「守卫不再沉默且带两边操作数」,⛔ 不判白屏修没修。
- t.base 复位 planned + 加边 t.instr2→t.base:盲的时候不许再测一遍基线。
- 判据字段形状照抄现有绿判据(cwd=${worktree}/time_budget_seconds/unjudgeable_exit_codes=[2]),⛔ 不猜。
- 证据(说明.md + baseline-20260822.json + 30 份 raw logcat)抄进 main 留档,也让 t.instr2 读得到。
preflight ok、dry-run frontier=[t.instr2]。
…case 的正解) 引擎这次把话说全了:t.base=frozen_no_new_case「判据红后未换新 case_id,且无有效人裁定 replay, 不许再进前沿空转」——这不是缺陷,是防空转守卫。解法我自己 skill 03-返修 里就写着 (AwaitingHuman 真实含义 = attempts 里残留 failed_retryable,清它),上一次打针漏了这一步。 - 只清 t.base 的 failed_retryable attempt(1→0),⛔ 没清 rounds、⛔ 没清 audit.route_hops(仍 4 跳)—— 轮次上限靠它们才有效(无等编排 leader 今夜特别提醒过,他们在这儿翻过车)。 - 判据红的证据没丢:pb-emu 的 说明.md、baseline-20260822.json、30 份 raw logcat 已在 2d72f6d 进 main, 失败诊断原文也在 perfbase-drive2.log 里。 - t.instr2 已 succeeded(仪表补盲落码);核过它归 t.rv.base 判,autopr 不会抢在评审前 land。 dry-run frontier=[t.base]。
pb-emu 第二轮把世界又收窄一层,且**自己收回了上一轮一条错误旁证**(conn.frames_sent=0 只在 delta 路径自增、空闲夹具恒 0,不能当证据)——这份诚实比一份基线值钱: - t.instr2 补的 ref_mismatch / no_open 两种留痕 30 次一条都没打 ⇒ SessionViewModel.onBinary 根本没被调用 ⇒ 帧没走到收帧口,不是被 ref 守卫吞的。 - 服务端仍坚称推了(perf_subscribe 60-80ms、每连接 snapshots_from_subscribe=1)。 - 剩两种世界:①WS 层没收到 ②收到了但 ConnectionManager.kt:617 的单一全局 listener 槽 为 null/被覆盖, 静默吞掉,上下游零日志。 t.instr3 只补这两处留痕(no_listener 带 listener_null= 两边操作数;ws_binary_recv 带 kind=/bytes=), ⛔ 不修白屏本身——回炉纪律:先让日志能一锤定音,再动手。 judge-instr3.sh 里 shellcheck 抓到我写的 A && B || C(SC2015)已改直白循环—— 判据自己的逻辑含糊,正是假绿的来源。 另记一条量测环境事实(不属本格,供后续裁定):同一构建两轮 layout_settled p50 差 3 倍 (0.54s vs 1.5-1.7s),差别来自宿主负载(load 9.16→11.89、free 一度 70MB)。 ⇒ 想立地板,先要有负载受控的量测环境,否则基线本身不可复现。
第三轮把丢帧点钉到一行之内,且席位**再次收回了自己上一轮的定位**: - ws_binary_recv 29/29 在场(kind=snapshot bytes>0)⇒ 不是 WS 没收到; - frame_ref 的 pane 号与所点夹具一一对应、与 daemon 侧 perf_subscribe 对得上 ⇒ 不是 ref 送错; - listener_null=0、no_listener 零命中 ⇒ 不是 listener 槽为空。ConnectionManager.kt:617 的 是它第二轮指认的丢帧点,这轮自己收回。 ⇒ 帧死在 SessionViewModel.onBinary 内部。 最后分不开的原因很朴素:instr2 与 instr3 两批留痕**从未在同一个构建里同时在场**。 t.instr4 只做合批 + 补一个操作数 listener_ref=(接帧者自己的 ref/实现类),与 frame_ref= 并排—— 两边操作数一齐出现,这题当场结束。⛔ 仍然只补仪表不修逻辑(回炉纪律)。⚠️ 连报三轮的量测环境问题,本轮首次带量化依据(host-load.tsv): 同一指标 layout_settled p50 第一轮≈537ms、第三轮 1450-1809ms,宿主 load1 均值 10.30 峰值 17.87。 ⇒ **立地板之前必须先有负载受控的量测窗口**,否则 ±10% 不倒退门形同虚设。 这条要在白屏定位收口后单独裁,不塞进本格。
…);开修复格 t.fixblank
铁证(frame_ref 与 listener_ref 首次同构建并排):
ev=ws_binary_recv emitted=0 kind=snapshot bytes=1137 frame_ref=…/default%0
ev=first_frame_recv emitted=0 reason=has_listener listener_null=0
listener_ref=dev.agentmirror.app.workspace.WorkspaceViewModel
⇒ 服务端推了、WS 收了、ref 对得上、槽非空——帧被交给了**列表页**的 VM 然后丢弃,
会话页 VM 一帧没拿到。这就是用户报了两天的「打开会话白屏」。
四轮收窄过程(每轮只加仪表不改逻辑,回炉纪律):
r9 30/30 缺 first_frame_recv/snapshot_applied,发现仪表自己有盲区
r14 补 ref_mismatch/no_open → 一条没打 ⇒ onBinary 没被调用(席位收回 frames_sent=0 那条错误旁证)
r16 补 ws_binary_recv/no_listener → listener_null=0 ⇒ 帧死在 onBinary 内部(席位收回 :617 的定位)
r18 合批 + listener_ref → 接帧者是 WorkspaceViewModel,定案
t.fixblank 先红后绿:路由红测两条(帧只投给订阅该ref的接收者 / 列表页占用槽位不影响会话页收帧)
必须先验红并贴原始输出;判据另加一条守卫——⛔ 修复时不许拆掉 PerfTrace 仪表,
量具没了就没法自证修好。t.base 换钥匙挂到修复格之后。
t.cperf 判**不可判(2)**(不是红):被测物 pr/perfbase-core 4ef1f0488 的基点是 e55e27f, 在仪表与白屏修复 land 进 main 之前 ⇒ APK dex 内 PerfTrace 符号 0 处、addBinaryListener 0 处, 三夹具各 1 次冷点开导航全成功但 logcat 每次 0 行。**不是性能差,是量不到。** 席位⛔ 没换夹具/没换次数/没换取数方式、⛔ 没手工合并切分树与 main 去凑一个能测的构建; 判据也没把不可判折进通过或失败——四态在这里各守其位。 根因是 leader 的账本排序错:依赖 land 结果的格与 land 竞速。 处置:t.core 换 worktree_id=wt-pb-core2 从当前 main(含 4e22f2f)重做切分, 派单正文点名⛔ 切分时不许拆掉仪表与按 ref 分发的修复;t.cperf 清 failed attempts 复位。 另把 t.instr4/PR#10 的 park 追加进 escalations(与 PR#6 同类:内容已随 fixblank 进 main, 再 land 自己就是冲突)。
用户 2026-08-22 令。上一轮我只删了 CLAUDE.md 里那句话,没换已经起着的席位—— 删字不等于换席位,pb-emu 一直还是 Opus 5 在跑,这次一并纠正。 remove-agent --force --confirm + add-agent 重建,pane 自证 Grok 4.6,账本 preflight 仍 ok。 新角色文件里点名了「开工先读前任四轮说明与现成脚本」——换席位=失忆,知识只能靠落盘传承。 副作用如实记:异源评审的 provider 那一层没有了,只剩不同席位/零上下文/不采信自报/判据哨兵。
实撞:我打针把 t.core 换到新树 wt-pb-core2(在含仪表+修复的 main 上重做切分), 但分支名仍按任务 id 推成 pr/perfbase-core,而旧树 wt-pb-core 还占着它 ⇒ git switch -c fatal: already exists ⇒ 封版每 60s 红一次。 worktree 才是隔离单位,分支名理应跟着它。改后 wt-pb-core2 → pr/perfbase-core2(PR #13), 旧分支与 PR #12 原样留着作为被取代的那次尝试,⛔ 没删没改没 force-push。 既有各格的分支名不变(wt-pb-app→app、wt-pb-srv→srv…),只有换过树的那格改名。
时间线:01:11 派 t.cperf 给 pb-emu → 01:2x 我按用户令把 pb-emu 从 Opus 5 重建成 grok-4.6 ⇒ remove-agent --force 连同在飞的派单一起销毁,新席位是空的、从没收到过它; 驱动器却还在 wait 那个 case_id,会一直等到 5400s 预算耗尽,日志上看不出异常 (投递回执早就 ok 了,⛔ ok:True 不是送达的又一个形态)。 读屏才认出来:新 pane 里只有 session_start,没有派单正文。 这是 leader 的操作错,不是席位也不是引擎。处置:清 t.cperf 的 attempts 换钥匙、 state=planned、revision++、重起驱动器;route_hops 与其它格 attempts 一律没动。 教训已写进 skill 03-返修:重建席位前先看驱动器有没有在等这个席位的 case; 判活别只看 pane 有没有提示符——新席位的空提示符和干完活的空提示符长得一样。
… 1 被记成 FAIL
judge-doc.sh 的成功分支 echo "PASS $F($N 行)" 里,$F 后紧跟全角「(」,
bash 3.2 把该多字节字符的字节并进变量名 ⇒ set -u 报 unbound variable ⇒ rc=1。
后果比崩溃更坏:**判据自己坏了却被记成「判据不通过」**,而不是不可判(2)。
t.close 就是这么被判红的——收账文书其实写没写都还没轮到检查。
同类写法在 9 个判据脚本里共 13 处,全部改成 ${VAR}(;sh -n 与 shellcheck 全清。
⚠️ 只有 judge-doc.sh 的在成功分支上,所以别的没被触发——这是运气,不是没病。
Synchronize tools/nodeprobe from 863c316 (auto-discovered .js observer, default pi-activity dir, web /api/status schema 1, unique-Pi missing channel health=normal). Replace Go title/process classifiers with a typed nodeprobe adapter, extend protocol/APP data models with four axes, and leave list/icon/animation/favorite UI unchanged.
Ordinary and favorite rows share one gesture owner: online short-press opens, offline short-press does not navigate, long-press shows exactly one favorite action. Working uses the existing busyDot lamp frames; idle is static; unknown/abnormal play no animation and do not show question marks or repeated 未知. The right side is the official Provider mark from the status-core DTO, with pinned LobeHub SVG bytes.
Workspace and favorites production routes now share one SessionRow (title + cwd, 66dp). Runtime AndroidSVG and LobeHub SVG copies are removed because the frozen visual source JPEG contains no Provider glyphs to crop. Canonical ids stay closed; no invented marks.
Florious95
force-pushed
the
pr/external-session-status-ui
branch
from
September 3, 2026 08:44
19749b5 to
10fbf39
Compare
Reuse icon() inline SVG fallbacks for claude_code/codex/grok/cursor. Copilot and Pi have no source glyph; those rows stay blank.
Exact drawable-nodpi bytes from 119b3a4c0ba5a9f540987c5ed275fd8e3ad978d9 (R.drawable.provider_pi / provider_copilot_color). No overlay, no LobeHub download.
Exact prior-app LICENSE/NOTICE blobs for the two reused PNGs only. KDoc @contract/@inv/@consumes on touched UI files; no icon regeneration.
Grok tiles use BRAND img slug grok, not icon() dead X switch. Pi working+normal DTO still maps to shared busyDot; no status-core change.
Florious95
force-pushed
the
pr/external-session-status-ui
branch
from
September 3, 2026 16:41
c937835 to
59970c5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
Independent external session-list UI stacked on accepted status-core (corral-core PR #70 exact head
23e0c4f1529b7b51192d6e65ecc62b3b517e2cf5). Ordinary session list and favorites list share one row:StatusChipbusyDot frames: alpha 1.0↔0.35,Motion.statusDotPulse/2, emphasized Reverse). Idle is the same lamp, static.provider. The APP does not guess. Unrecognized ids draw nothing.收藏or取消收藏; favorites long-press shows exactly取消收藏. Icons/lamp do not swallow events.Does not reuse closed PR #67, and does not modify PR #69 / status-core / upload.
Tests
./gradlew :app:testDebugUnitTest --rerun-tasks --no-build-cache→ 612 tests, 0 failed, 0 skipped. Covers virtual-clock working vs idle frames, six canonical providers + unknown, four-axis projection, two-list short/long/offline/disabled actions, no star / no question / no repeated 未知.emulator-5578AVDpi_ext_status_ui_api35,./gradlew :app:connectedDebugAndroidTest --rerun-tasks --no-build-cacheclassExternalSessionListGestureTest→ 2/0.ExternalSessionListAcceptanceActivity.Assets
Pinned
@lobehub/icons-static-svg@1.94.0(tarball SHA-256a813cbb544624f51344ceab00b21c3fb0e760a989453ca447c502098698b1ec2). Exact SVG SHA-256s and MIT LICENSE SHA-256add9d7531d1b21646317a8958e38fc727506fa39d24bdecb44154d943c82753ainthird_party/lobehub-icons-static-svg-1.94.0/PROVENANCE.txt. Runtime copies underapp/app/src/main/assets/provider-icons/are byte-identical. Renderer: vendoredandroidsvg 1.4Apache-2.0 jar.Risks
23e0c4f. If that parent moves, this PR must be re-frozen.Local monorepo head:
94459b900e998ab49a89f299585ac831de090bd2on064bf78b84737e5fca941876f0598d4704d85b2f.