Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/lib/actions/sandbox/connect-flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ describe("connectSandbox flow", () => {
);
});

it("probe-only reports failure to create new authority after secure absence is proven (#8942)", async () => {
it("probe-only completes recovery after secure absence is proven and reports unpublished evidence (#9280)", async () => {
const harness = createConnectHarness({
readinessDecision: {
kind: "fallback",
Expand All @@ -620,18 +620,21 @@ describe("connectSandbox flow", () => {
fenceFailed: true,
recoveryBlocked: false,
},
readinessPublicationResult: { kind: "evidence-failed" },
});

await expect(harness.connectSandbox("alpha", { probeOnly: true })).rejects.toThrow(
"process.exit(1)",
);

expect(harness.checkAndRecoverSpy).not.toHaveBeenCalled();
expect(harness.errorSpy.mock.calls.flat().join("\n")).toContain(
"no prior launch-readiness evidence can be accepted, but new launch-readiness authority could not be created",
expect(harness.checkAndRecoverSpy).toHaveBeenCalledOnce();
expect(harness.ensureLiveSandboxSpy).toHaveBeenCalled();
expect(harness.publishLaunchReadinessSpy).toHaveBeenCalledOnce();
expect(harness.errorSpy).toHaveBeenCalledWith(
" Probe failed: complete probe and recovery succeeded, but final launch-readiness evidence could not be verified or published.",
);
expect(harness.errorSpy.mock.calls.flat().join("\n")).not.toContain(
"prior launch-readiness evidence could not be fenced",
"new launch-readiness authority could not be created",
);
});

Expand Down
16 changes: 12 additions & 4 deletions src/lib/actions/sandbox/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1285,11 +1285,19 @@ export async function connectSandbox(
console.log(` Probe complete: launch readiness is healthy for '${sandboxName}'.`);
return;
}
if (readiness.fenceFailed && readiness.authorityUnsupported !== true) {
// Refuse recovery only when a prior epoch might exist and could not be
// durably rotated. When the authority and receipt are both securely
// absent but new authority creation fails (fenceFailed without
// recoveryBlocked), the documented contract runs the complete preflight
// and recovery and reports the publication failure afterwards, exactly
// as `launch` does for the same decision (#9280).
if (
readiness.fenceFailed &&
readiness.authorityUnsupported !== true &&
readiness.recoveryBlocked
) {
console.error(
readiness.recoveryBlocked
? " Probe failed: complete probe and recovery did not run because prior launch-readiness evidence could not be fenced. Repair the current user's secure OS runtime authority and NemoClaw state permissions, then retry."
: " Probe failed: no prior launch-readiness evidence can be accepted, but new launch-readiness authority could not be created. Repair the current user's secure OS runtime authority and NemoClaw state permissions, then retry.",
" Probe failed: complete probe and recovery did not run because prior launch-readiness evidence could not be fenced. Repair the current user's secure OS runtime authority and NemoClaw state permissions, then retry.",
);
process.exit(1);
}
Expand Down
Loading