Skip to content

Commit 30d30fd

Browse files
authored
fix(e2e): separate launch readiness states (#9053)
## Summary Separate the OpenClaw launch helper's initial readiness text from its post-reply readiness text. The live TUI first reports `gateway connected | idle`, then reports `connected | idle` after a reply; the helper now waits for each exact state before its corresponding input and requires exact normalized-line equality for the post-reply state. PR #9034 was externally merged while its live acceptance remained blocked. This fix-forward repairs acceptance infrastructure only. It does not retroactively claim that #9034 passed live acceptance; the exact probe and two-turn PTY acceptance will be rerun for the latest PR commit. ## Related Issue Related to #9023. Follow-up to #9034. ## Live acceptance status On commit `c601bb61675f6561b3fdcd5131b633d9d45bd371`, the repaired readiness sequence reached the exact reply, post-reply `connected | idle`, `/exit`, and `gateway disconnected: closed | idle`. Final launch status remained blocked by the separate portable/rootless-Podman cleanup recurrence tracked in #9054, which is a recurrence of #8584/#8585 rather than a defect in this helper. This PR remains acceptance-infrastructure repair only. It does not claim that the overall #9023 two-turn live acceptance passed. ## Changes - Add a distinct optional post-reply readiness value to the native launch helper. - Configure OpenClaw live callers with startup `gateway connected | idle` and post-reply `connected | idle` states. - Add a realistic regression that rejects `/exit` until the post-reply state is visible. - Require the post-reply readiness marker to equal one normalized terminal line, so a repeated `gateway connected | idle` cannot satisfy `connected | idle`. ## Type of Change - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [ ] Docs updated for user-facing behavior changes - [x] Docs not applicable — justification: This changes live E2E orchestration only; it does not change a user command, configuration, default, diagnostic, runtime contract, or supported workflow. - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `no-docs-needed` - Evidence: The exact four-file E2E-helper diff was independently reviewed. It affects test orchestration only, and its names, test title, and diagnostic comply with `WRITING.md`. - Agent: Codex Desktop <!-- docs-review-head-sha: 1c3a274 --> <!-- docs-review-agents-blob-sha: e30afb2 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `npx vitest run --project e2e-support test/e2e/support/launch-agent-turn.test.ts`: 5 passed, 1 platform-skipped - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: Not applicable to this narrow live-helper correction; focused support tests and all normal hooks passed. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com> --------- Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>
1 parent 6fc834c commit 30d30fd

4 files changed

Lines changed: 20 additions & 5 deletions

File tree

test/e2e/live/full-e2e.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ async function runOpenClawLaunchTurnAfterRecovery(input: {
148148
env: env(PORTABLE_PROFILE ? { DOCKER_HOST: "" } : {}),
149149
exitCommand: "/exit",
150150
host: input.host,
151+
postReplyReadyText: "connected | idle",
151152
readyText: "gateway connected | idle",
152153
redactionValues: input.redactionValues,
153154
sandboxName: SANDBOX_NAME,

test/e2e/live/launch-agent-turn.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ has_exact_reply() {
110110
has_post_reply_ready() {
111111
normalized_response | awk \
112112
-v expected="$NEMOCLAW_LAUNCH_EXPECTED_REPLY" \
113-
-v ready="$NEMOCLAW_LAUNCH_READY_TEXT" '
113+
-v ready="$NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT" '
114114
{
115115
line = $0
116116
sub(/^[[:space:]]+/, "", line)
117117
sub(/[[:space:]]+$/, "", line)
118118
if (line == expected) reply = 1
119-
if (reply && index(line, ready) != 0) found = 1
119+
if (reply && line == ready) found = 1
120120
}
121121
END { exit found ? 0 : 1 }
122122
'
@@ -132,7 +132,7 @@ for _ in {1..180}; do
132132
sleep 1
133133
done
134134
135-
if [[ "$reply_seen" = 1 && -n "$NEMOCLAW_LAUNCH_READY_TEXT" ]]; then
135+
if [[ "$reply_seen" = 1 && -n "$NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT" ]]; then
136136
post_reply_ready_seen=0
137137
for _ in {1..60}; do
138138
if has_post_reply_ready; then
@@ -187,6 +187,7 @@ export interface LaunchAgentTurnOptions {
187187
env: NodeJS.ProcessEnv;
188188
exitCommand?: string;
189189
host: HostCliClient;
190+
postReplyReadyText?: string;
190191
readyText?: string;
191192
redactionValues: string[];
192193
sandboxName: string;
@@ -210,6 +211,7 @@ export async function runLaunchAgentTurn(
210211
NEMOCLAW_LAUNCH_EXIT_COMMAND: options.exitCommand ?? "",
211212
NEMOCLAW_LAUNCH_EXPECTED_REPLY: options.expectedReply ?? EXPECTED_REPLY,
212213
NEMOCLAW_LAUNCH_PROMPT: options.prompt ?? PROMPT,
214+
NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT: options.postReplyReadyText ?? "",
213215
NEMOCLAW_LAUNCH_READY_TEXT: options.readyText ?? "",
214216
NEMOCLAW_LAUNCH_SANDBOX: options.sandboxName,
215217
TERM: "xterm-256color",

test/e2e/live/launch-readiness-lease-acceptance.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ test.runIf(process.platform === "linux" && SANDBOX_NAME.length > 0)(
3838
env: process.env,
3939
exitCommand: "/exit",
4040
host,
41+
postReplyReadyText: "connected | idle",
4142
readyText: "gateway connected | idle",
4243
redactionValues: [],
4344
sandboxName: SANDBOX_NAME,

test/e2e/support/launch-agent-turn.test.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ exit ${exitStatus}
5151
NEMOCLAW_LAUNCH_EXIT_COMMAND: closeAfterReply ? "" : "/exit",
5252
NEMOCLAW_LAUNCH_EXPECTED_REPLY: "PONG",
5353
NEMOCLAW_FIXTURE_REPLY: reply,
54+
NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT: "",
5455
NEMOCLAW_LAUNCH_PROMPT: "prompt",
5556
NEMOCLAW_LAUNCH_READY_TEXT: "",
5657
NEMOCLAW_LAUNCH_SANDBOX: "sandbox",
@@ -87,6 +88,7 @@ it.runIf(process.platform === "linux")(
8788
env: {},
8889
exitCommand: "/exit",
8990
host: host as never,
91+
postReplyReadyText: "connected | idle",
9092
readyText: "gateway connected | idle",
9193
redactionValues: [],
9294
sandboxName: "alpha",
@@ -108,11 +110,18 @@ it.runIf(process.platform === "linux")(
108110
"/exit",
109111
"/exit",
110112
]);
113+
expect(calls.slice(1).map((call) => call.env?.NEMOCLAW_LAUNCH_READY_TEXT)).toEqual([
114+
"gateway connected | idle",
115+
"gateway connected | idle",
116+
]);
117+
expect(
118+
calls.slice(1).map((call) => call.env?.NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT),
119+
).toEqual(["connected | idle", "connected | idle"]);
111120
},
112121
);
113122

114123
it.runIf(process.platform !== "win32")(
115-
"waits for OpenClaw idle before the prompt and again before the exit command (#9023)",
124+
"waits for 'gateway connected | idle' before the prompt and 'connected | idle' before exit (#9023)",
116125
() => {
117126
const fixtureRoot = mkdtempSync(join(tmpdir(), "nemoclaw-launch-turn-ready-"));
118127
const scriptStub = join(fixtureRoot, "script");
@@ -136,11 +145,12 @@ fi
136145
printf 'gateway connected | idle\n' | tee -a "$capture"
137146
IFS= read -r -d $'\r' _
138147
printf 'PONG\n' | tee -a "$capture"
148+
printf 'gateway connected | idle\n' | tee -a "$capture"
139149
if IFS= read -r -t 1 -d $'\r' _; then
140150
echo "exit arrived before post-reply readiness" >&2
141151
exit 1
142152
fi
143-
printf 'gateway connected | idle\n' | tee -a "$capture"
153+
printf 'connected | idle\n' | tee -a "$capture"
144154
IFS= read -r -d $'\r' exit_command
145155
[[ "$exit_command" == "/exit" ]]
146156
exit 0
@@ -161,6 +171,7 @@ exit 0
161171
NEMOCLAW_LAUNCH_EXIT_COMMAND: "/exit",
162172
NEMOCLAW_LAUNCH_EXPECTED_REPLY: "PONG",
163173
NEMOCLAW_LAUNCH_PROMPT: "prompt",
174+
NEMOCLAW_LAUNCH_POST_REPLY_READY_TEXT: "connected | idle",
164175
NEMOCLAW_LAUNCH_READY_TEXT: "gateway connected | idle",
165176
NEMOCLAW_LAUNCH_SANDBOX: "sandbox",
166177
PATH: `${fixtureRoot}:${process.env.PATH ?? ""}`,

0 commit comments

Comments
 (0)