fix(status): print the scope beside the projection-route verdict - #4594
huangruiteng wants to merge 6 commits into
Conversation
`loopx status` and `loopx doctor` both print a `healthy` value for runtime
projection routes, but they read different denominators: doctor collects the
shared global registry and reports on every route in it, while status scopes
the same collector to its own registry and, with `--goal-id`, to a single
goal. Neither surface named that scope, so an operator comparing the two read
a flat contradiction — doctor red, status green — with nothing in either line
to explain it.
The projection now carries the route count it was computed over, and both
renderers print the registry and denominator beside the flag:
- runtime_projection_routes: healthy=False (registry=<global>, goals=44), details=loopx doctor
- runtime_projection_routes: healthy=True (registry=<global>, goal=<goal>, goals=1)
Verified on this machine before the change: `loopx doctor` reported
`healthy=False` (44 goals, 13 missing) while `loopx status` reported
`healthy=True`; with the fix, an unfiltered `status` reads the same registry as
doctor and agrees at `healthy=False (goals=44)`.
`runtime_projection_routes` grows by exactly one key. It is a deliberately
budgeted hot-path payload, so the scope is kept to the denominator the flag
actually needs instead of inlining the full diagnostics; the interface-budget
smoke still passes with the dashboard status payload at 19155/19500 chars.
Validated: refresh-state shared-runtime projection smoke, shared-runtime
material projection smoke, and hot-path interface budget smoke pass;
`pytest tests/ -k "status or doctor or projection_route"` reports 311 passed,
1 skipped, with two pre-existing `sqlite-current`/`sqlite-missing` failures
reproduced unchanged on origin/main; `loopx check` is clean.
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
The first revision of this change printed `registry=...`, `goal=...` and
`goals=...` on the projection-route line. That grew the agent-facing markdown
rows by 119-125 characters, and
`examples/control_plane/cli-output-base-head-differential-smoke.py` allows 32
characters and 64 bytes per row:
surface/status/crowded/markdown chars grew by 121; allowance is 32
surface/status/multi_agent/markdown chars grew by 125; allowance is 32
surface/status/small/markdown chars grew by 119; allowance is 32
The line now carries only the denominator — `healthy=False (goals=44)`,
`healthy=True (goals=1)` — which is the fact that explains the disagreement:
a scoped goal is one route, the fleet is forty-four. The registry stays out of
this row because `doctor` already names it along with the full counts map on
the unhealthy reading, and the status JSON keeps `registry` at the top level
for machine readers. Growing the budget instead would have been a gate change
for a presentation fix.
Validated: `cli-output-base-head-differential-smoke` ok with base=102,
candidate=102, candidate_only=0, review_required=0; the refresh-state
shared-runtime projection, shared-runtime material projection and hot-path
interface budget smokes pass; `pytest -k "status_markdown or cli_output or
projection_route"` reports 85 passed.
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
86c2945 to
259789c
Compare
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
动机
本车道在 2026-09-17 的旅程资格里记录了同一会话内的矛盾:`loopx status` 报 `runtime_projection_routes: healthy=True`,`loopx doctor` 报 `runtime_projection_routes_healthy: False`,两个面都不说明各自读的是哪个范围,也没有对账规则。这是典型“读数缺分母”的问题:两者共用同一个 `healthy` 键,但一个读项目内 goal 路由、另一个读共享全局 registry。
改动思路
不做仲裁、不改判定逻辑,而是让每个面把“这个布尔值是在多少条路由上算出来的”一并说出来:`status` 在既有行尾补 `(goals=N)`,受 32 字符 agent 输出预算约束;`doctor` 额外给出 registry 路径、goals 数与完整 counts 映射,便于直接对账。两侧渲染都由 smoke 断言。
具体改动
- loopx/control_plane/status/collection.py:`runtime_projection_routes` 增加 `goal_count`,取自同一路由诊断,缺省为 0。
- loopx/presentation/renderers/status_markdown.py:`healthy={bool}` 后按需附 `(goals=N)`,并加 docstring 说明为何 registry 路径留给 doctor。
- loopx/doctor.py:`runtime_projection_routes_healthy` 行补 `registry=`、`goals=`、`counts=`(JSON、键排序)。
- 三个 smoke 由“等于 {"healthy": bool}”改为断言 `healthy` 与 `goal_count`,并新增 status/doctor 一致性断言与两处渲染文本断言。
对主干的风险
产品行为变化限于新增的展示字段与 renderer 文案:原先断言精确字典形状的 smoke 已同步改为逐键断言,避免把新增字段读成回归。scope 语义没有被重新定义,只是显式化;未改变任何门禁或判定结果。hot-path 输出预算 smoke 已在该 head 通过,说明 `(goals=N)` 未超出 agent 面行预算。
我的整体评价
以“补分母、两边都点名范围”而不是“改一个面附和另一个面”的方式修掉了我此前记录的 operator-truth 缺口,方向和最小性都对,且用 smoke 把两面对账固定下来。建议在该 head 的必过检查转绿后合并;合并后即可关闭依赖它的 todo_845adbcd6703。
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Head: 0061271
English verdict: APPROVE
…on-route-scope-20260917 Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
动机
loopx doctor 与 loopx status 都会为 runtime projection routes 打印一个 healthy,但两边的分母不同,而且两边都没说自己用的是哪个分母。复现出来的结果就是:doctor 报 runtime_projection_routes_healthy: False(全局 registry,44 条路由里 13 条 source_registry_missing),同一次会话里 status 报 healthy=True(项目 registry、且可再按 --goal-id 收紧)——两个数在各自口径下都对,读的人却会以为其中一个错了。本车道在本地管家旅程里如实记下了这个分歧(todo_845adbcd6703)。
改动思路
让每条打印自己那一层:status 行只带分母 healthy=False (goals=44) / healthy=True (goals=1),registry 名与完整计数留给 doctor。这样既点名“这是哪一层的健康度”,又不去动 agent-facing 的输出预算——这一点很关键,因为第一版把 registry=、goal=、goals= 都塞进 status 行后,markdown 行涨了 119–125 字符,被 examples/control_plane/cli-output-base-head-differential-smoke.py 以每行 32 字符 / 64 字节的额度拒掉。
具体改动
loopx/presentation/renderers/status_markdown.py、loopx/control_plane/status/collection.py、loopx/doctor.py:status 侧输出收紧为分母,doctor 侧保留 registry 与完整 counts map。examples/control_plane/hot-path-interface-budget-smoke.py、refresh-state-shared-runtime-projection-smoke.py、shared-runtime-material-projection-smoke.py:把新契约与该头的输出预算一起钉住。
对主干的风险
这是面向操作者的输出改动,风险是“为了说清楚而把 agent-facing 行撑大”,而第一版正是踩了这个坑,所以修正版把信息量压到分母上、并用差分 smoke 把额度钉住。本次复审在更新后的 head 上实测(该 head 只是把 origin/main 合进来,六个被评审文件在旧 head 与新 head 之间逐字节一致,已用内容哈希核对):
uv run --extra test python examples/control_plane/cli-output-base-head-differential-smoke.py→ok base_ref=origin/main base=102 candidate=102 candidate_only=0 review_required=0uv run --extra test python -m pytest -k "status_markdown or cli_output or projection_route" -q→ 85 passed, 9708 deselected
我的整体评价
把一个真实存在、且会让操作者得出相反结论的口径分歧变成“各自说明自己那一层”,并用输出预算门把“说清楚”和“别撑大”同时钉住,方向是对的。建议在该 head 的必过检查全绿的前提下合并。
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Head: 8888a92
English verdict: APPROVE
|
Superseded by #4636. Retained the useful route count, but a denominator alone cannot distinguish equal-sized scopes. The replacement makes doctor report its actual diagnostic registry and filter metadata, reuses status registry/Goal/activation context, and labels the global drill-down. Real CLI fixtures cover same-route agreement and different registries with identical counts but different health. Output budgets and all 19 premerge checks pass. The route-health computation and write behavior are unchanged; closing this branch to keep one merge target. |
The disagreement this fixes
loopx doctorandloopx statusboth print ahealthyvalue for runtime projection routes. They read different denominators, and neither line said so.Reproduced on this machine before the change:
Both values are arithmetically correct.
doctorcollects the shared global registry and marks the fleet unhealthy because 13 goals reportsource_registry_missing;statusscopes the same collector to its own registry, and further to one goal under--goal-id. Pointed at the same registry,statusalready agreed with doctor:So the operator-visible contradiction was the missing scope, not a wrong number: an owner running
doctorafterstatussaw red next to green with nothing to reconcile them.Change
runtime_projection_routesin the status projection gains the route count it was computed over.--format json.After:
The status line carries only the denominator, because the agent-facing markdown
rows allow 32 extra characters each;
doctornames the registry and the fullcounts map on the unhealthy reading.
Interface budget
runtime_projection_routesgrows by exactly one key. This is a deliberately budgeted hot-path payload, so the scope is limited to the denominator the flag needs rather than inlining the full diagnostics. The interface-budget smoke passes with the dashboard status payload at19155/19500chars.Validation
examples/control_plane/refresh-state-shared-runtime-projection-smoke.py— added the reconciliation invariant:statusanddoctormust agree onhealthyandgoal_countover the same registry, and doctor's markdown must name its scope.examples/control_plane/shared-runtime-material-projection-smoke.py,examples/control_plane/hot-path-interface-budget-smoke.py— updated the assertions that pinned the previous lossy{"healthy": ...}shape.pytest tests/ -k "status or doctor or projection_route"— 311 passed, 1 skipped. Twotest_task_graph_topology.pysqlite-current/sqlite-missingfailures are environmental and reproduce unchanged onorigin/main.loopx check— clean.Boundary
Public-safe: no private state, credentials, local paths or raw evidence. No control-plane decision, permission or scoring behavior changes; only the projection's self-description and the two rendered lines.