fix(gstack2): treat hard runtime failure as not-ready in capability readiness

A capability whose binary launches while the managed runtime hard-fails
(e.g. skill-API mismatch) was reported as `degraded`/ok:true/exit 0, diverging
from plain `gstack doctor` (ok:false/exit 1) for the identical report. Split
the branch so runtime `warn` stays `degraded` and runtime `fail` maps to
`failed`, and add a regression test for the runtime-fail + capability-pass case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sinabina
2026-07-21 14:12:54 -07:00
co-authored by Claude Opus 4.8
parent d240b0fa6a
commit 52a6101706
2 changed files with 9 additions and 1 deletions
@@ -52,11 +52,18 @@ describe("capability readiness", () => {
{ id: "managed-runtime", status: "pass", message: "active" },
{ id: "capability:diagram", status: "fail", message: "launcher metadata missing" },
]), "diagram");
// A hard runtime failure under a launchable capability is not a warning:
// it must not report ok:true, matching plain `gstack doctor`'s exit code.
const runtimeFailed = capabilityReadiness(report([
{ id: "managed-runtime", status: "fail", message: "incompatible skill API" },
{ id: "capability:browser", status: "pass", message: "launched" },
]), "browser");
expect(ready).toMatchObject({ ok: true, readiness: { status: "ready" } });
expect(degraded).toMatchObject({ ok: true, readiness: { status: "degraded" } });
expect(failed).toMatchObject({ ok: false, readiness: { status: "failed" } });
expect(failed.consent.install.status).toBe("required-after-preview");
expect(runtimeFailed).toMatchObject({ ok: false, readiness: { status: "failed" } });
});
test("reports physical iOS as unsupported without turning off pure judgment", () => {