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
7 changes: 4 additions & 3 deletions test/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,10 @@ The host must provide the util-linux `script` command and GNU `timeout` command.

The helper rebuilds the candidate CLI, runs `connect --probe-only`, and then
runs two `launch` sessions during the same fixed lease.
Each pseudo-terminal session sends a unique prompt, requires the exact reply,
sends `/exit`, and requires process exit status `0`.
The helper uses exact terminal behavior instead of a wall-clock pass threshold.
Each real pseudo-terminal session sends two distinct messages and `/exit`, then
requires process exit status `0`. The OpenClaw session store must append two
nonempty `user` and `assistant` record pairs in one session. The helper does not
compare message content. Terminal output is a bounded failure diagnostic only.
Deterministic unit tests separately prove selection of the complete preflight
and lease paths, stale-producer exclusion, the fixed time-unsafe quarantine,
refusal to recover when prior evidence cannot be durably fenced, and the named
Expand Down
8 changes: 3 additions & 5 deletions test/e2e/live/full-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
fullE2eInferenceProbeEvidence,
runFullE2eInferenceProbe,
} from "./full-e2e-inference-probe.ts";
import { runLaunchReadinessLeaseTurns } from "./launch-agent-turn.ts";
import { runOpenClawLaunchReadinessLeaseTurns } from "./launch-agent-turn.ts";
import { bindApprovedPrBaseForBaseImageComparison } from "./pr-base-comparison.ts";

const SANDBOX_NAME = process.env.NEMOCLAW_SANDBOX_NAME ?? "e2e-full";
Expand Down Expand Up @@ -141,15 +141,13 @@ async function runOpenClawLaunchTurnAfterRecovery(input: {
);
expect(recovery.exitCode, resultText(recovery)).toBe(0);

await runLaunchReadinessLeaseTurns({
await runOpenClawLaunchReadinessLeaseTurns({
artifactName: "phase-4-openclaw-launch-turn",
cliCommand: USE_PREINSTALLED_LAUNCHABLE ? "nemoclaw" : process.execPath,
...(!USE_PREINSTALLED_LAUNCHABLE ? { cliEntrypoint: CLI_ENTRYPOINT } : {}),
env: env(PORTABLE_PROFILE ? { DOCKER_HOST: "" } : {}),
exitCommand: "/exit",
host: input.host,
postReplyReadyText: "gateway connected | idle",
readyText: "gateway connected | idle",
redactionValues: input.redactionValues,
sandboxName: SANDBOX_NAME,
});
Expand Down Expand Up @@ -391,7 +389,7 @@ test("full e2e: install, onboard, inference, cli operations, and cleanup", {
"direct hosted inference and sandbox inference.local both respond",
...(process.platform === "linux"
? [
"a recovered OpenClaw sandbox completes a /exit launch turn through inference.local and restores the mutable config permission contract",
"each of two PTY launches records two ordered structured turns and restores the mutable config permission contract",
]
: []),
"nemoclaw logs produces output and cleanup removes registry state",
Expand Down
4 changes: 0 additions & 4 deletions test/e2e/live/gateway-guard-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,6 @@ test("gateway recovery restores /tmp guard chain after pod-recreate wipe (#2701)
});
expect(trustedRecovery.timedOut, resultText(trustedRecovery)).toBe(false);
expect(trustedRecovery.exitCode, resultText(trustedRecovery)).toBe(0);
expect(resultText(trustedRecovery)).toMatch(
/Probe complete: (?:recovered OpenClaw gateway|OpenClaw gateway is running)/,
);
const restartStateLockPlan = await sandbox.exec(
instance.sandboxName,
["python3", "-c", OPENCLAW_STATE_LOCK_PLAN_PROBE],
Expand Down Expand Up @@ -501,7 +498,6 @@ test("gateway recovery restores /tmp guard chain after pod-recreate wipe (#2701)
});
expect(legacyRecovery.timedOut, resultText(legacyRecovery)).toBe(false);
expect(legacyRecovery.exitCode, resultText(legacyRecovery)).toBe(0);
expect(resultText(legacyRecovery)).toContain("Probe complete: recovered OpenClaw gateway");
const legacyStateLockPlan = await sandbox.exec(
instance.sandboxName,
["python3", "-c", OPENCLAW_STATE_LOCK_PLAN_PROBE],
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/live/hermes-e2e-phases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const HERMES_E2E_PHASES = [
"prepare clean Hermes runner",
"install and onboard Hermes sandbox",
"validate sandbox layout, health, and skill activation",
"restart Hermes gateway, validate supervision, and complete two launch turns",
"restart Hermes gateway and validate supervision",
"exercise hosted and inference.local routes",
"validate CLI manifest and locked-config behavior",
"finalize Hermes sandbox resources",
Expand Down
17 changes: 4 additions & 13 deletions test/e2e/live/hermes-e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type { ShellProbeResult } from "../fixtures/shell-probe.ts";
import { assertHermesCliAdapterLiveContract, stripAnsi } from "./hermes-cli-adapter-live.ts";
import { HERMES_E2E_PHASES } from "./hermes-e2e-phases.ts";
import { assertHermesSkillLifecycle } from "./hermes-skill-lifecycle.ts";
import { runLaunchReadinessLeaseTurns } from "./launch-agent-turn.ts";
import { expectPackageDatabaseReadOnly } from "./package-database-read-only.ts";

const SANDBOX_NAME = process.env.NEMOCLAW_SANDBOX_NAME ?? "e2e-hermes";
Expand Down Expand Up @@ -599,7 +598,7 @@ test("hermes-e2e: install.sh onboards Hermes and proves health plus live inferen
expect(httpStatusOk(dashboardInternal.stdout)).toBe(true);
}

progress.phase("restart Hermes gateway, validate supervision, and complete two launch turns");
progress.phase("restart Hermes gateway and validate supervision");
// Phase 5: host-mediated Hermes gateway restart. This validates the
// runtime contract behind #2426 against a real OpenShell/Hermes sandbox:
// The installed supervision tree controls the gateway process, direct
Expand Down Expand Up @@ -1256,17 +1255,9 @@ test("hermes-e2e: install.sh onboards Hermes and proves health plus live inferen

expect(routingTopologyCaptures).toBe(2);

await (process.platform === "linux"
? runLaunchReadinessLeaseTurns({
artifactName: "phase-5-hermes-launch-turn-after-recovery",
cliCommand: "nemoclaw",
env,
host,
redactionValues,
sandboxName: SANDBOX_NAME,
})
: Promise.resolve());

// OpenClaw launch qualification now reads its structured JSONL session
// store. Hermes owns a different SQLite contract, so this target must not
// infer Hermes replies from terminal copy through the OpenClaw helper.
progress.phase("exercise hosted and inference.local routes");
// Phase 6: live inference through both the external provider and the
// sandbox's inference.local route.
Expand Down
17 changes: 2 additions & 15 deletions test/e2e/live/issue-2478-crash-loop-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ async function runProbeOnly(
},
sandboxName: string,
artifactName: string,
): Promise<"connect" | "supervisor"> {
): Promise<void> {
const result = await host.nemoclaw([sandboxName, "connect", "--probe-only"], {
artifactName,
env: probeEnv(),
Expand All @@ -239,18 +239,6 @@ async function runProbeOnly(
result.exitCode,
`${artifactName} failed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
).toBe(0);
const connectRecovery = `Probe complete: recovered OpenClaw gateway in '${sandboxName}'.`;
const supervisorRecovery = `Probe complete: OpenClaw gateway is running in '${sandboxName}'.`;
const recoveryPath = result.stdout.includes(connectRecovery)
? "connect"
: result.stdout.includes(supervisorRecovery)
? "supervisor"
: null;
expect(
recoveryPath,
`${artifactName} did not observe a healthy gateway after termination\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
).not.toBeNull();
return recoveryPath!;
}

async function terminateGatewayIdentity(
Expand Down Expand Up @@ -369,7 +357,7 @@ test("gateway recovery restores the guard chain and keeps the recovered process
preRecoveryIdentity!,
"functional-recovery-terminate-gateway",
);
const recoveryPath = await runProbeOnly(
await runProbeOnly(
host,
instance.sandboxName,
"functional-recovery-connect-probe-only",
Expand Down Expand Up @@ -398,7 +386,6 @@ test("gateway recovery restores the guard chain and keeps the recovered process
await artifacts.writeJson("functional-recovery-summary.json", {
initialIdentity,
preRecoveryIdentity,
recoveryPath,
recoveredIdentity,
stableIdentity,
stabilitySeconds: STABILITY_SECONDS,
Expand Down
Loading
Loading