Files
workinf_Blender_Wasm/my-tools/view_ui_parity_progress.py
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

189 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""只读查看桌面 Blender UI → Web Blender UI 完全对标主线。
复用既有进度快照;不启动执行器、写运行状态、构建、部署或运行功能验证。
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from view_wbv2_progress import (
FOCUS_NAMES, STATE_NAMES, WORK_NAMES, duration, interval_value, local_time,
snapshot as project_snapshot,
)
HEALTH_NAMES = {
'normal': '进程和心跳正常(不代表功能推进正常)',
'unknown': '缺少运行记录', 'stopped': '执行器已停止',
'process_missing': '执行器退出或 PID 已复用',
'process_unknown': '进程身份无法确认', 'heartbeat_stale': '心跳过期',
'worker_exited_pending_handoff': '工作进程退出,等待交接',
}
SIGNALS = {
'provider_retry_or_reconnect': '有服务失败或重连记录',
'artifact_command_active': '正在生成产物',
'candidate_refresh_due': '候选已到刷新条件',
'source_idle_over_3m': '超过三分钟未观察到源码修改(不单独判为空转)',
'source_progress_stale': '近期源码修改间隔较长',
'focus_repeated_no_progress': '重复无进展或阻塞,需要改变定位策略',
}
def snapshot(recent_limit=3):
# Use the same bounded reads, exact gap IDs and remaining-over-completed
# reconciliation as the original viewer; do not fork scheduler semantics.
data = project_snapshot(recent_limit)
ui = data.get('uiParity') or {}
return {
'observedAt': data['observedAt'], 'task': data.get('task'),
'scope': ui.get('phase'), 'planPath': ui.get('planPath'),
'goal': ui.get('goal'), 'currentModule': ui.get('currentModule'),
'associatedModules': ui.get('associatedModules', []),
'modules': ui.get('modules', []), 'runner': data.get('runner', {}),
'provider': data.get('provider', {}), 'candidate': data.get('candidate'),
'latestImplementation': data.get('latestImplementation', {}),
'recent': data.get('recent', []), 'build': data.get('build', {}),
'focuses': data.get('progress', {}).get('focuses', []),
'alerts': data.get('alerts', []), 'notes': data.get('notes', []),
'validation': data.get('validation', 'UNKNOWN'),
'acceptance': data.get('acceptance', 'UNKNOWN'),
'basis': '只读既有交接;计数仅覆盖已登记子项,不是界面覆盖率或验收结果',
}
def display(data, module_id=None, details=False):
print(f"桌面 Blender UI → Web Blender UI 对标 | {local_time(data['observedAt'])}")
print(f"任务:{data.get('task') or '未知'}")
runner = data['runner']
if runner:
print(f"执行:{STATE_NAMES.get(runner.get('recordedState'), runner.get('recordedState'))};"
f"{HEALTH_NAMES.get(runner.get('health'), '状态未知')}")
print(f"当前第 {runner.get('cycle')} 轮 | {FOCUS_NAMES.get(runner.get('focus'), runner.get('focus'))} | "
f"{WORK_NAMES.get(runner.get('workState'), runner.get('workState') or '未知')}")
print(f"本轮已用 {duration(runner.get('elapsedSeconds'))};心跳 {duration(runner.get('heartbeatAgeSeconds'))};"
f"最近动作 {duration(runner.get('lastActionAgeSeconds'))};源码空闲 {duration(runner.get('sourceIdleSeconds'))}")
print(f"活动命令 {runner.get('activeCommandCount', 0)};构建/打包 {runner.get('activeArtifactCommands', 0)};"
f"本轮观察到源码文件 {len(runner.get('observedProductionFiles') or [])} 个")
if runner.get('reason'):
print(f"原因:{runner['reason']}")
if data['currentModule']:
print(f"当前模块:{data['currentModule']}")
else:
print('当前精确模块未记录;执行方向关联:' + ('、'.join(data['associatedModules']) or '未记录'))
provider = data['provider']
if provider:
print(f"服务:失败记录 {provider.get('consecutiveFailures', 0)};重连 {'是' if provider.get('reconnecting') else '否'};"
f"退避 {duration(provider.get('retrySeconds'))};错误 {provider.get('error') or '无当前分类'}")
for focus in data['focuses']:
if focus.get('deferredPathSeconds') or focus.get('retrySeconds'):
print(f"路径等待:{focus.get('title')} / {focus.get('deferredPathId') or '当前方向'};"
f"暂缓上限 {duration(focus.get('deferredPathSeconds', 0))},重试 {duration(focus.get('retrySeconds', 0))}")
for signal in data['alerts']:
print('执行信号:' + SIGNALS.get(signal, HEALTH_NAMES.get(signal, signal)))
print('\n模块状态(只统计专属登记项;零项不表示未实现,也不表示已完成):')
for module in data['modules']:
if module_id and module.get('id') != module_id:
continue
print(f" [{module['id']}] {module.get('title', '')}:{module['status']}")
if module.get('gaps'):
counts = module['counts']
print(f" 登记子项:已报告实现 {counts['implemented_unverified']} / 定位 {counts['locate']} / 待实现或集成 {counts['open']}")
elif module.get('progressHandoff'):
handoff = module['progressHandoff']
print(f" 模块交接:已接入 {len(handoff.get('completedRoutes') or [])} 条 / "
f"剩余 {len(handoff.get('remainingRoutes') or [])} 条;来源 {handoff.get('reportPath', 'manifest')}")
print(f" 样式与完整入口对标:{module.get('visualParity', '未记录')};整模块尚未确认完成")
if details or module_id:
print(f" 路由:{module.get('route') or '尚无模块专属路由记录'}")
if module.get('relatedHandoffPath'):
print(f" 相关方向交接:{module['relatedHandoffPath']}(不代表整模块交接)")
if module.get('ownerDocs'):
print(f" 文档:{module['ownerDocs']}")
for gap in module.get('gaps', []):
implemented = gap.get('status') == 'implemented_unverified'
phase = '定位' if gap.get('phase') == 'locate' else '实现'
label = '已报告实现,未验证' if implemented else '尚未闭合'
print(f" [{gap['id']}] {phase} / {label}:{gap.get('entry')}")
if not implemented:
finding = gap.get('latestFinding') or {}
residual = gap.get('remainingSubroutes') or []
print(f" 剩余:{';'.join(residual) if residual else finding.get('reason') or gap.get('missing') or '未记录'}")
print(f" 下一步:{finding.get('action') or gap.get('nextAction') or '未记录'}")
if gap.get('sourcePaths'):
print(' 源码落点:' + '、'.join(gap['sourcePaths']))
if module.get('progressHandoff'):
handoff = module['progressHandoff']
latest = handoff.get('latestImplementation') or {}
if latest:
print(f" 最近补漏:{latest.get('reportPath', 'manifest')};候选 {latest.get('candidatePath', '未记录')};验证 {latest.get('validation', 'UNKNOWN')}")
for route in handoff.get('remainingRoutes') or []:
print(f" 模块余项:{route}")
print(f" 模块下一步:{handoff.get('nextAction', '未记录')}")
latest = data['latestImplementation']
latest_time = latest.get('at')
print(f"\n最近源码交接:第 {latest.get('cycle', '—')} 轮 / {local_time(latest_time)}")
print(' ' + (latest.get('businessEntry') or latest.get('workflow') or '尚无记录'))
for row in data['recent']:
print(f" [{row['cycle']}] {row.get('summary') or row.get('businessEntry') or '未提供摘要'}")
candidate = data['candidate']
if candidate:
print(f"\n匹配候选记录:{candidate.get('path')}")
gap = candidate.get('sourceGap')
threshold = candidate.get('refreshGap')
pending = str(gap) if gap is not None else '未知'
print(f" 待组合 {pending} 轮;刷新阈值 {threshold or '未配置'};"
f"{'已到刷新条件' if candidate.get('refreshDue') else '未到已知刷新条件'}")
print(f" 部署:{candidate.get('deployment') or '未知'};源码/运行对应关系未验证")
else:
print('\n匹配候选:尚无可读取记录')
if details and data['build'].get('logPath'):
print('最近构件日志:' + data['build']['logPath'])
print(f"验证:{data['validation']};验收:{data['acceptance']}。不以心跳、轮次或构建判断 UI 已完全对标。")
for note in data['notes']:
print('提示:' + note)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--json', action='store_true', help='JSON 输出;持续模式为每行一个快照')
parser.add_argument('--watch', action='store_true', help='持续刷新,Ctrl+C 退出')
parser.add_argument('--interval', type=interval_value, default=10, metavar='秒', help='刷新间隔,默认 10 秒,至少 1 秒')
parser.add_argument('--module', metavar='ID', help='只显示指定模块;同时展开该模块细节')
parser.add_argument('--details', action='store_true', help='展开路由、缺口、下一动作、源码和报告位置')
parser.add_argument('--recent', type=int, choices=range(0, 11), default=3, metavar='0..10', help='最近交接条数,默认 3')
args = parser.parse_args()
try:
while True:
data = snapshot(args.recent)
if args.module:
if args.module not in {item['id'] for item in data['modules']}:
print(f'模块 {args.module} 未在当前可读记录中登记。', file=sys.stderr)
for note in data['notes']:
print(note, file=sys.stderr)
return 1
if args.json:
data['modules'] = [item for item in data['modules'] if item['id'] == args.module]
data['moduleFilter'] = args.module
if args.json:
print(json.dumps(data, ensure_ascii=False, indent=None if args.watch else 2), flush=True)
else:
if args.watch and sys.stdout.isatty():
print('\033[2J\033[H', end='')
display(data, args.module, args.details)
sys.stdout.flush()
if not args.watch:
return 0 if data.get('task') and data.get('runner') else 1
time.sleep(args.interval)
except (KeyboardInterrupt, BrokenPipeError):
return 0
if __name__ == '__main__':
raise SystemExit(main())