合并受阻
变更摘要
本 PR 修复 Issue #91:沙箱不可用时启动仍报 ready、界面绿灯仅代表前端与本地 API 连通的问题。变更将启动链路已有的沙箱探测结果透出到 /health,并在 Web 界面新增「计算就绪」状态区分。packages/schema 的 RunnerHealth 新增必填字段 sandboxUsable 与可选 sandboxDetail;services/runner 的 createRunnerServer 接收 sandboxUsable/sandboxDetail 参数并在 /health 响应中返回,startRunnerServer 捕获 detectSandboxCapability 结果并透传;apps/web 通过新增的 ApiHealthResponse 类型与 getApiHealth() 方法轮询 health,在沙箱不可用时以琥珀色连接指示器与警告横幅提示,但 composer 仍可操作(提示不阻断)。
主要改动
- 健康状态字段扩展:
RunnerHealth新增必填sandboxUsable: boolean与可选sandboxDetail?: string,用于表达沙箱能否创建隔离执行环境及不可用原因。 - Runner 探测结果透传:
createRunnerServer新增sandboxUsable/sandboxDetail参数并写入/health响应,startRunnerServer将detectSandboxCapability的sandboxUsable与detail透传下去,既有降级警告语义不变。 - Web 健康轮询与状态派生:
apps/web/src/App.tsx新增每 30s 轮询getApiHealth(),由runner.sandboxUsable === false驱动computeUnavailable与computeUnavailableDetail状态,请求异常时回落为不可用状态为false。 - 连接指示器与警告横幅: 连接指示器在已连接但计算不可用时切换为
connection degraded(琥珀色)并显示「计算不可用」chip,消息区上方新增compute-unavailable-notice横幅(role="alert")提示沙箱未就绪、仍可浏览历史。 - API 客户端与文案样式:
ArtifactsApiClient新增ApiHealthResponse类型与getApiHealth()方法读取完整/api/health;i18n/messages.ts补充 en/zh-CN 三组文案,styles/shell.css新增琥珀色圆点与横幅样式。
(注:services/api 的三个测试文件中 3 处 mock RunnerHealth 补 sandboxUsable: true 属测试适配,未单列。)


The pipeline(pipeline number:160) is running. Please wait a moment...


AI 自动检视,仅供参考,问题都需要人工评估分析.
变更摘要
对应 Issue #91:Runner 启动探针已能发现 bubblewrap/Seatbelt 建不出沙箱,但结果从未进 UI,绿灯只表示「前端↔API 可达」。本 PR 把 detectSandboxCapability 的结论经 Runner /health → API /api/health 透传到 Web,用琥珀色连接指示 + 顶部 banner 标出「计算不可用」,composer 仍可操作(提示不阻断)。
相对 origin/main 仅 1 commit / 9 文件:RunnerHealth 增 sandboxUsable(必填)与 sandboxDetail?;startRunnerServer 把探测结果传入 createRunnerServer;API 侧若干 mock 补字段;Web 每 30s 轮询 getApiHealth(),并补 en/zh-CN 与 CSS。
做法是否合理
链路与兼容处理整体对齐问题描述:
- 缺省→绿灯:Web 用
"sandboxUsable" in runner再判=== false,旧 Runner 无该字段、或runner: { status: "unavailable" }时不会误报琥珀色;API 请求失败在catch里清掉computeUnavailable,避免 API 不可达时假阳性。 - 透传:API
/api/health直接把runnerClient.health()结果(或{ status: "unavailable" })塞进响应,不剥字段;Runner/health在sandboxUsable === false时仍报status: "ok"(进程健康但算力不可用),UI 明确按sandboxUsable区分,合理。 - 提示不阻断:
computeUnavailable只驱动 chip/banner,未改 composer 的disabled条件,与 commit 说明一致。 - 测试:API mock 已跟上必填字段;本地未跑完整 typecheck/单测(工作树无依赖安装),按 diff 看 schema/
satisfies RunnerHealth与 mock 补齐方向正确。若后续想加固,可补一条「sandboxUsable: false→ 健康聚合仍含字段」或 Web 轮询分支的轻量断言,非合入阻塞。
可合


✅ 流水线 96607d632507461390d7fe9d38808543 执行成功。
可在 https://gitcode.com/openJiuwen/sciencediscovery/pull/160/check 查看完整构建日志。
在 PR 评论中发送 rerun 可重新触发 CI 流水线。
| 任务名称 | 子任务 | 状态 | 详情 |
|---|---|---|---|
| 代码检查 | SCA(开源合规) | PASSED | 点此跳转 |
| Anti-poison(防投毒) | PASSED | 点此跳转 | |
| CodeCheck(静态检查) | PASSED | 点此跳转 | |
| Blacklist(禁用词) | PASSED | 点此跳转 | |
| UT | host tier | PASSED | 公开日志 |
| guest tier (QEMU TCG sandbox) | PASSED | 公开日志 | |
| ST | - | PASSED | 公开日志 |
| Binary | x86_64 debug package | PASSED | binary · SHA256SUMS |
| aarch64 debug package | PASSED | binary · SHA256SUMS |


The pipeline(pipeline number:160) is running. Please wait a moment...


AI 自动检视,仅供参考,问题都需要人工评估分析.
相对上轮的变化
相对已检视 HEAD f52c81a1,本轮为 rebase 到较新基线后的 force-push:git range-diff / patch-id 与上一轮完全一致,Issue #91 的 9 文件改动(Runner 探针 → /health → API → Web 轮询、琥珀指示与 banner、缺省不误报)本身无新增逻辑。相对当前 origin/main(73015b2e,fix(web) i18n 清残留)仍 behind 1 / ahead 1;merge-tree 显示 App 等与 main 有重叠但无冲突标记,合入前再 rebase 一次即可,不挡审。
上轮结论仍成立:缺省/API 失败不误报 amber、提示不阻断与 Issue 一致。
可合


✅ 流水线 adc75682da7041b798e8273b652c473b 执行成功。
可在 https://gitcode.com/openJiuwen/sciencediscovery/pull/160/check 查看完整构建日志。
在 PR 评论中发送 rerun 可重新触发 CI 流水线。
| 任务名称 | 子任务 | 状态 | 详情 |
|---|---|---|---|
| 代码检查 | SCA(开源合规) | PASSED | 点此跳转 |
| Anti-poison(防投毒) | PASSED | 点此跳转 | |
| CodeCheck(静态检查) | PASSED | 点此跳转 | |
| Blacklist(禁用词) | PASSED | 点此跳转 | |
| UT | host tier | PASSED | 公开日志 |
| guest tier (QEMU TCG sandbox) | PASSED | 公开日志 | |
| ST | - | PASSED | 公开日志 |
| Binary | x86_64 debug package | PASSED | binary · SHA256SUMS |
| aarch64 debug package | PASSED | binary · SHA256SUMS |


评审意见(基于 main 分支实测核查)
一句话结论
建议小改(approve-with-nits):实现本身正确、防御性好,但新增 UI 状态零测试覆盖,且描述中关于 seatbelt 的声明与代码不符。
描述核查
- 文件数/行数/单 commit 属实:9 files、+73/-3、1 commit(实际 head 为
4814718,非描述所称f52c81a,base 也非13fce1a,描述里的验证提交信息已过期)。 - “含 seatbelt 不可用时的 detail 透传到 /health”不属实:diff 只在 bwrap 分支(server.ts
@@ -1114处)赋值sandboxUsable/sandboxDetail;seatbelt 分支(@@ -1086上下文,startRunnerServer内)不可用时仍直接throw,runner 根本不会起来,/health永远不可能在 macOS 上报sandboxUsable:false。琥珀态实际仅 Linux/bwrap 可达。 - “既有降级警告语义不变”属实:
--disable-userns缺失、/proc回退两条 WARNING 分支未被改动,仅在前面加了赋值。 - “API 不可达 →
setComputeUnavailable(false),维持红灯逻辑”属实:catch 分支已验证;红灯由独立errorstate 驱动。e2e 声明也属实:现有规格只断言.connection.bad计数为 0(test/journey-local-token-onboarding.spec.ts:204),.connection.degraded不匹配。 - “3 个 mock 补字段”:实际是 3 个文件里的 4 处字面量(run-cancel.test.ts、model-usage-api.test.ts、server.test.ts ×2),且全部只写
sandboxUsable: true,没有任何用例走到false分支。
问题清单
- [主要] 全 PR 无任何新测试。mock 只是满足必填字段;
sandboxUsable:false → 琥珀点 + 横幅、轮询 catch、旧 runner 无字段保持绿色等新逻辑(apps/web/src/App.tsx diff@@ -1425,33与@@ -4404处)完全无覆盖,与自检清单“已有 UT 充分覆盖”不符。建议补:web 测试 mockgetApiHealth断言.connection.degraded/.compute-unavailable-notice渲染与否;runner 侧断言/health含sandboxUsable。 - [次要] runner 进程不可达时(
runner.status === "unavailable",App.tsx 轮询处)UI 仍显绿色。结合 seatbelt 分支直接 throw,Issue #91 的 macOS 场景依旧看不到“计算不可用”。建议把runner === undefined/status==="unavailable"也并入computeUnavailable,或至少修正描述并留 issue。 - [次要] services/api/src/server.test.ts diff
@@ -526,7处替换行丢失缩进:runnerVersion: "test", sandbox: "bubblewrap", sandboxUsable: true,顶格,破坏该文件既有对齐(第二处@@ -3020缩进正常)。纯格式问题。 - [建议] apps/web/src/styles/shell.css 新增
.connection.degraded i { background: #d4a017 }硬编码色值,而 token 表已有--warning-accent: #d19a32(styles/tokens.css:72)且横幅其余部分用的就是 warning 变量;建议改用var(--warning-accent)保持主题一致。 - [建议] App.tsx catch 分支未清空
computeUnavailableDetail(残留旧值,因横幅被computeUnavailable门控而无实害);tooltip 的{detail}缺省时回退整句 banner 文案,略冗长。
亮点
"sandboxUsable" in runner的 in-guard 让前端对不含新字段的旧 runner 保持绿色,向后兼容干净。- 轮询 effect 取消安全(
active标志 +clearInterval,client经 memo 稳定不会重订阅),横幅与 tooltip 均以 React 文本节点/title渲染 runner 侧 detail,无注入风险,role="alert"可被读屏播报。
VERDICT: approve-with-nits


What type of PR is this?
/kind feature
What does this PR do / why do we need it:
修复 Issue #91:沙箱不可用时启动仍报 ready、界面绿灯仅代表连通,缺少真实计算就绪提示。
问题分析:
runPreflight已探测sandboxUsable,但serve()丢弃结果,照常打印 "ScienceDiscovery is ready";startRunnerServer用detectSandboxCapability探测到沙箱不可用时只打一行 WARNING,/health不暴露该结果;Connection OK),不代表沙箱可用、科学计算环境完备或模型额度有效;实现(9 files,把启动链路已有的探测结果透出到 Web 界面,不改底层配置结构):
RunnerHealth新增sandboxUsable: boolean(必填)+sandboxDetail?: string(沙箱不可用时给出原因)。createRunnerServer新增sandboxUsable/sandboxDetail参数;startRunnerServer捕获detectSandboxCapability的结果(含 seatbelt 不可用时的detail),透传到/health响应。既有的降级警告(--disable-userns缺失、/proc回退等)语义不变。RunnerHealth对象补sandboxUsable: true满足新必填字段。ApiHealthResponse类型 +getApiHealth()方法,返回完整/api/health(含 runner 沙箱状态)。App.tsx每 30s 轮询 health,computeUnavailable状态由runner.sandboxUsable === false驱动;.connection.degraded),tooltip 带原因,chip 文案「计算不可用」,与「前端连通(绿)」/「需要处理(红)」区分开;.compute-unavailable-notice警告横幅(role="alert"):「当前环境无法执行分析任务。沙箱环境未就绪。你仍可浏览历史会话与产物。」+ 具体 detail。范围说明:Issue 中「计算不可用时应整站阻止发起分析,还是允许只读浏览」标注为待产品拍板的待定项,本 PR 采用「提示不阻断」策略——横幅警告但 composer 仍可操作,留给后续决策单独实现。
Which issue(s) this PR fixes:
Fixes #91
Test Plan and Test result:What scenarios were tested, and what were the verification results(Function, performance, reliability, etc.):
验证提交:f52c81a(9 files, +73/-3,单 commit,基于最新 upstream/main 13fce1a)
run-cancel.test.js:通过(含 mock health 更新)sandboxUsable: false且带sandboxDetail→ 绿灯变琥珀、chip 显示「计算不可用」、横幅出现并附原因sandboxUsable: true或缺省)→ 保持绿色,无横幅,不误报setComputeUnavailable(false),维持既有红灯逻辑.connectionclass 计数)。Self-checklist:(请自检,在[ ]内打上x,我们将检视你的完成情况,否则会导致pr无法合入)