fix(ci): repair full release validation regressions (#117447)

* fix(worktrees): reject repositories without commits

* test(memory-wiki): allow agent isolation runtime

* test(ios): await queued approval retry
This commit is contained in:
Vincent Koc
2026-08-01 22:55:13 +08:00
committed by GitHub
parent 5ad7c969b8
commit 7cc2e6d830
5 changed files with 41 additions and 5 deletions

View File

@@ -1684,9 +1684,9 @@ private func overrideNotificationServingPreference(_ enabled: Bool) -> () -> Voi
}
appModel._test_setUnifiedExecApprovalGetResponse(makePendingExecApprovalJSON(approvalID))
await appModel._test_reconcileWatchExecApprovalCache(reason: "operator_reconnected")
let deadline = ContinuousClock().now.advanced(by: .seconds(2))
let deadline = ContinuousClock().now.advanced(by: .seconds(10))
while await !writeGate.hasStarted(), ContinuousClock().now < deadline {
await Task.yield()
try await Task.sleep(for: .milliseconds(10))
}
let writeCount = await writeGate.callCount()
#expect(writeCount == 1)

View File

@@ -263,5 +263,5 @@ describe("agent-scoped memory-wiki tools", () => {
} finally {
clearMemoryPluginState();
}
}, 240_000);
}, 360_000);
});

View File

@@ -76,6 +76,8 @@ export class WorktreeSnapshotError extends Error {
this.snapshotError = snapshotError;
}
}
export class WorktreeRepositoryError extends Error {}
const SNAPSHOT_REF_PREFIX = "refs/openclaw/snapshots";
const log = createSubsystemLogger("agents/worktrees");
@@ -181,9 +183,13 @@ async function resolveRepositoryFromRealPath(
): Promise<ResolvedRepository> {
const rootResult = await runGit(requested, ["rev-parse", "--show-toplevel"]);
if (rootResult.code !== 0) {
throw new Error(`not a git checkout: ${requestedLabel}`);
throw new WorktreeRepositoryError(`not a git checkout: ${requestedLabel}`);
}
const sourceRoot = await fs.realpath(rootResult.stdout.trim());
const headResult = await runGit(sourceRoot, ["rev-parse", "--verify", "HEAD^{commit}"]);
if (headResult.code !== 0) {
throw new WorktreeRepositoryError(`git checkout has no commits: ${requestedLabel}`);
}
const commonRaw = await requireGit(sourceRoot, ["rev-parse", "--git-common-dir"]);
const commonDir = await fs.realpath(
path.isAbsolute(commonRaw) ? commonRaw : path.resolve(sourceRoot, commonRaw),

View File

@@ -15,7 +15,7 @@ import { resolveSandboxRuntimeStatus } from "../../agents/sandbox/runtime-status
import { ensureAgentWorkspace } from "../../agents/workspace.js";
import { insideGitCheckout } from "../../agents/worktrees/git.js";
import { slugifyWorktreeTitle } from "../../agents/worktrees/name.js";
import { managedWorktrees } from "../../agents/worktrees/service.js";
import { managedWorktrees, WorktreeRepositoryError } from "../../agents/worktrees/service.js";
import { resolveAgentMainSessionKey } from "../../config/sessions/main-session.js";
import { sessionEntryForkedFromParent } from "../../config/sessions/session-entry-lineage.js";
import type { SessionEntry } from "../../config/sessions/types.js";
@@ -375,6 +375,14 @@ export const sessionCreateHandlers: GatewayRequestHandlers = {
provisionedSessionWorktree = true;
}
} catch (error) {
if (error instanceof WorktreeRepositoryError) {
respond(
false,
undefined,
errorShape(ErrorCodes.INVALID_REQUEST, "agent workspace is not a git checkout"),
);
return;
}
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(error)));
return;
}

View File

@@ -1405,6 +1405,28 @@ test("sessions.create rejects worktrees for non-git agent workspaces", async ()
}
});
test("sessions.create rejects worktrees for agent workspaces without a commit", async () => {
const workspace = await makeNonGitTempDir("openclaw-session-unborn-workspace-");
await execFileAsync("git", ["init", workspace]);
testState.agentConfig = { workspace };
await createSessionStoreDir();
try {
const created = await directSessionReq(
"sessions.create",
{ agentId: "main", worktree: true },
{ client: { connect: { scopes: ["operator.admin"] } } as never },
);
expect(created.ok).toBe(false);
expect(created.error).toMatchObject({
code: "INVALID_REQUEST",
message: "agent workspace is not a git checkout",
});
} finally {
testState.agentConfig = undefined;
}
});
test("sessions.create stores dashboard model, thinking, and parent linkage, and creates a transcript", async () => {
const { storePath } = await createSessionStoreDir();
agentDiscoveryMock.enabled = true;