Skip to content
Open
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
1 change: 0 additions & 1 deletion src/lib/onboard/machine/core-flow-phases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,6 @@ export function createSandboxOnboardFlowPhase<
context: mergeSandboxCreatedContext(context, {
session: sandboxStateResult.session,
sandboxName: sandboxStateResult.sandboxName,
recreateJournalHandoff: Boolean(options.recreateJournalTargetIntentFingerprint),
webSearchConfig: sandboxStateResult.webSearchConfig,
webSearchConfigChanged: sandboxStateResult.webSearchConfigChanged,
hermesToolGateways: sandboxStateResult.hermesToolGateways,
Expand Down
2 changes: 0 additions & 2 deletions src/lib/onboard/machine/final-flow-phases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ export function createFinalOnboardFlowPhases<
? options.finalization.webSearchProvider(context.webSearchConfig)
: null,
portableProfileSelected: context.session?.checkpoint?.profile.value === "portable",
recreateJournalHandoff: context.recreateJournalHandoff,
deps: finalizationDeps,
});
return { result: finalizationResult.stateResult };
Expand All @@ -158,7 +157,6 @@ export function createFinalOnboardFlowPhases<
? options.finalization.webSearchProvider(context.webSearchConfig)
: null,
portableProfileSelected: context.session?.checkpoint?.profile.value === "portable",
recreateJournalHandoff: context.recreateJournalHandoff,
deps: finalizationDeps,
});
return { result: postVerifyResult.stateResult };
Expand Down
2 changes: 0 additions & 2 deletions src/lib/onboard/machine/flow-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import type { OnboardStateHandlerResult } from "./runner";
export interface OnboardFlowContext<Agent = unknown, Gpu = unknown, SandboxGpuConfig = unknown> {
resume: boolean;
fresh: boolean;
recreateJournalHandoff?: boolean;
session: Session | null;
agent: Agent;
recordedSandboxName: string | null;
Expand Down Expand Up @@ -92,7 +91,6 @@ export interface ProviderModelSelectedContextUpdate {
export interface SandboxCreatedContextUpdate {
session: Session | null;
sandboxName: string;
recreateJournalHandoff?: boolean;
webSearchConfig: WebSearchConfig | null;
webSearchConfigChanged: boolean;
hermesToolGateways: string[];
Expand Down
51 changes: 30 additions & 21 deletions src/lib/onboard/machine/handlers/finalization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,35 +541,44 @@ describe("finalization handlers", () => {
expect(calls.reportReadiness).toHaveBeenCalledWith(false);
});

it("settles ordinary OpenClaw pairing after recovery and before verification (#9844)", async () => {
const { deps, calls } = createDeps();
it("settles ordinary OpenClaw pairing after recovery and before verification (#10479)", async () => {
let releasePairing!: () => void;
const pairingPending = new Promise<void>((resolve) => {
releasePairing = resolve;
});
const events: string[] = [];
const settleOrdinaryPairing = vi.fn(async () => {
events.push("pairing-started");
await pairingPending;
events.push("pairing-settled");
return { kind: "settled" as const };
});
const verifyDeployment = vi.fn(async () => {
events.push("verify");
return { ok: true };
});
const { deps, calls } = createDeps({
settleOrdinaryOpenClawPairing: settleOrdinaryPairing,
verifyDeployment,
});

await runFinalizationHandlers(baseOptions(deps));
const finalization = runFinalizationHandlers(baseOptions(deps));
await vi.waitFor(() => expect(events).toContain("pairing-started"));
expect(events).not.toContain("verify");
releasePairing();
await finalization;

expect(calls.settleOrdinaryPairing).toHaveBeenCalledExactlyOnceWith("my-assistant");
expect(calls.settleOrdinaryPairing.mock.invocationCallOrder[0]).toBeGreaterThan(
expect(settleOrdinaryPairing).toHaveBeenCalledExactlyOnceWith("my-assistant");
expect(settleOrdinaryPairing.mock.invocationCallOrder[0]).toBeGreaterThan(
calls.recoverProcesses.mock.invocationCallOrder[0],
);
expect(calls.settleOrdinaryPairing.mock.invocationCallOrder[0]).toBeLessThan(
expect(settleOrdinaryPairing.mock.invocationCallOrder[0]).toBeLessThan(
calls.recoverProcesses.mock.invocationCallOrder[1],
);
expect(calls.recoverProcesses.mock.invocationCallOrder[1]).toBeLessThan(
calls.verify.mock.invocationCallOrder[0],
verifyDeployment.mock.invocationCallOrder[0],
);
});

it("does not settle ordinary OpenClaw pairing during an inner rebuild handoff (#9844)", async () => {
const { deps, calls } = createDeps();

const result = await runFinalizationHandlers({
...baseOptions(deps),
recreateJournalHandoff: true,
});

expect(result.stateResult.type).toBe("complete");
expect(calls.settleOrdinaryPairing).not.toHaveBeenCalled();
expect(calls.ensureAgentDashboard).toHaveBeenCalledWith("my-assistant", null);
expect(calls.verify).toHaveBeenCalledOnce();
expect(events).toEqual(["pairing-started", "pairing-settled", "verify"]);
});

it("does not run OpenClaw pairing settlement for Hermes (#9844)", async () => {
Expand Down
19 changes: 10 additions & 9 deletions src/lib/onboard/machine/handlers/finalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export interface FinalizationStateOptions<Agent, VerifyChain, VerificationResult
webSearchEnabled: boolean;
webSearchProvider: WebSearchVerifyProvider | null;
portableProfileSelected?: boolean;
recreateJournalHandoff?: boolean;
deps: {
ensureAgentDashboardForward(sandboxName: string, agent: Agent): Promise<number> | number;
persistDashboardPort(sandboxName: string, dashboardPort: number): void;
Expand Down Expand Up @@ -178,7 +177,6 @@ export async function handleFinalizationState<Agent, VerifyChain, VerificationRe
sandboxName,
agent,
portableProfileSelected,
recreateJournalHandoff,
stagedLegacyKeys,
migratedLegacyKeys,
deps,
Expand All @@ -194,10 +192,12 @@ export async function handleFinalizationState<Agent, VerifyChain, VerificationRe
portableProfileSelected,
deps.readRegistryAgent,
);
// Rebuild recreates the container from the image, which wipes the
// machine-local pairing state (`identity` and `devices` are declared
// `backup: false` and removed on destroy), so the pairing gate must run on
// rebuild handoff exactly as on fresh onboarding (#10479).
const ordinaryOpenClawPairingRequired =
portableAgent === "ordinary" &&
selectedAgentName(agent) === "openclaw" &&
recreateJournalHandoff !== true;
portableAgent === "ordinary" && selectedAgentName(agent) === "openclaw";
// Reaching finalization means the policy-preset step was confirmed, so it is
// now safe to register this sandbox as the default (#4614).
deps.setDefaultSandbox(sandboxName);
Expand Down Expand Up @@ -251,7 +251,6 @@ export async function handlePostVerifyState<Agent, VerifyChain, VerificationResu
webSearchEnabled,
webSearchProvider,
portableProfileSelected,
recreateJournalHandoff,
deps,
}: FinalizationStateOptions<
Agent,
Expand All @@ -265,10 +264,12 @@ export async function handlePostVerifyState<Agent, VerifyChain, VerificationResu
portableProfileSelected,
deps.readRegistryAgent,
);
// Rebuild recreates the container from the image, which wipes the
// machine-local pairing state (`identity` and `devices` are declared
// `backup: false` and removed on destroy), so the pairing gate must run on
// rebuild handoff exactly as on fresh onboarding (#10479).
const ordinaryOpenClawPairingRequired =
portableAgent === "ordinary" &&
selectedAgentName(agent) === "openclaw" &&
recreateJournalHandoff !== true;
portableAgent === "ordinary" && selectedAgentName(agent) === "openclaw";
let verificationDiagnostics: string[] = [];
let deploymentHealthy = true;
if (portableAgent !== "ordinary") {
Expand Down
139 changes: 0 additions & 139 deletions src/lib/onboard/machine/rebuild-pairing-handoff.test.ts

This file was deleted.

27 changes: 27 additions & 0 deletions test/e2e/live/rebuild-openclaw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { shellQuote } from "../../../src/lib/core/shell-quote";
import { containsAnswer } from "../../helpers/e2e-answer-assertions.ts";
import { buildAvailabilityProbeEnv } from "../fixtures/availability-env.ts";
import { assertCleanupSucceededOrAbsent } from "../fixtures/cleanup-resources.ts";
import { assertExitZero as expectExitZero, resultText } from "../fixtures/clients/command.ts";
Expand All @@ -19,6 +20,7 @@ import {
writeJsonFile,
} from "../fixtures/file-state.ts";
import { CLI_ENTRYPOINT, REPO_ROOT } from "../fixtures/paths.ts";
import { parseOpenClawAgentText } from "../fixtures/openclaw-agent-output.ts";
import type { ShellProbeResult } from "../fixtures/shell-probe.ts";
import { createOldBaseBuildContext } from "./rebuild-openclaw-old-base-context.ts";

Expand Down Expand Up @@ -750,6 +752,31 @@ print(json.dumps({'seeded': saved == os.environ['PRE_REBUILD_GATEWAY_TOKEN'], 'h
// Phase 7: state preservation, upgrade, token rotation, backup hygiene, and
// policy-preset preservation assertions.
progress.phase("validate upgraded state policy inference and backup hygiene");
const agentTurn = await host.nemoclaw(
[
SANDBOX_NAME,
"agent",
"--agent",
"main",
"--json",
"--session-id",
`e2e-rebuild-oc-${Date.now()}-${process.pid}`,
"-m",
"What is 6 multiplied by 7? Reply with only the integer, no extra words.",
],
{
artifactName: "phase-7-agent-inference-after-rebuild",
env: cliEnv(apiKey),
redactionValues: [apiKey],
timeoutMs: 120_000,
},
);
expectExitZero(agentTurn, "OpenClaw agent inference after rebuild");
expect(
containsAnswer(parseOpenClawAgentText(agentTurn.stdout), "42"),
resultText(agentTurn),
).toBe(true);

const markerRead = await sandbox.exec(SANDBOX_NAME, ["cat", MARKER_FILE], {
artifactName: "phase-7-read-workspace-marker",
env: dockerContextEnv(),
Expand Down
1 change: 1 addition & 0 deletions test/e2e/mock-parity.json
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@
"live": "test/e2e/live/rebuild-openclaw.test.ts",
"fast": [
"src/lib/actions/sandbox/rebuild-flow-lifecycle.test.ts",
"src/lib/onboard/machine/handlers/finalization.test.ts",
"test/e2e/support/e2e-cleanup-resources.test.ts",
"test/e2e/support/e2e-clients.test.ts"
]
Expand Down
Loading