ci: accept master's release-verify lanes on the 2026.1006.0-beta.0 source

Stable promotion runs master's release-verify.yml against this older source
commit. Since this beta was cut, master split the server lane into
general-server-without-chat-or-native-runner plus a cached native-runner lane
(#15619) and moved the db project into its own general-workspaces-c lane
(#15831). This source's test runner rejected both group names, so every
server shard and the workspaces-c job failed in the stable verify chain.

Mirror those two groups here with the same lane membership: the explicit
server group excludes the chat and native-runner suites (both run in their
own lanes), and workspaces-c runs @paperclipai/db alone while workspaces-b
drops it. Local callers and the existing groups are unchanged. No shipped
package or runtime code changes.
This commit is contained in:
devinfoley committed 2026-10-12 18:20:52 +00:00
1 parent 22a3ea3414
commit 5121e61f8e
2 files changed
+81 -18

No files matched your search

@@ -428,3 +428,40 @@ test("filtered collection must match the exact assigned case identities, includi
assert.throws(() => assertSelectedTests(expected, actual, lineShardFile));
}
});
// Release verification on master (release-verify.yml after #15619 and #15831)
// selects the without-chat-or-native-runner server group plus a separate
// native-runner lane, and runs the db project in its own workspaces-c lane.
// This source predates those lanes, so the groups are mirrored here so the
// stable promotion of this commit verifies under master's workflow contract.
for (const caller of ["Release", "Cloud readiness", "another caller"]) {
test(`release server, chat, and native lanes cover every server suite once under ${caller}`, () => {
const env = { GITHUB_WORKFLOW: caller };
const full = dryRunJson(["--mode", "general", "--group", "general-server", "--shard-index", "0", "--shard-count", "1"], env);
const files = Array.from({ length: 10 }, (_, index) => dryRunJson([
"--mode", "general", "--group", "general-server-without-chat-or-native-runner",
"--shard-index", String(index), "--shard-count", "10",
], env)).flatMap((shard) => shard.selectedGeneralServerSuites);
const native = dryRunJson(["--mode", "general", "--group", "general-server-native-runner"], env);
const combined = [...files, chatSuitePath, ...native.selectedGeneralServerSuites];
assert.equal(new Set(combined).size, combined.length, "lanes must not overlap");
assert.deepEqual(combined.sort(), full.selectedGeneralServerSuites.sort());
});
}
test("workspaces-c runs the db project alone and workspaces-b no longer carries it", () => {
const workspacesC = dryRunJson(["--mode", "general", "--group", "general-workspaces-c"]);
assert.deepEqual(workspacesC.workspaceProjects, ["@paperclipai/db"]);
assert.equal(workspacesC.workspacesVitestShard, null);
const workspacesB = dryRunJson(["--mode", "general", "--group", "general-workspaces-b"]);
assert.ok(!workspacesB.workspaceProjects.includes("@paperclipai/db"), "db must not run twice");
assert.ok(workspacesB.workspaceProjects.length > 0, "workspaces-b must still run its other projects");
const workspacesA = dryRunJson(["--mode", "general", "--group", "general-workspaces-a"]);
const all = [...workspacesA.workspaceProjects, ...workspacesB.workspaceProjects, ...workspacesC.workspaceProjects];
assert.equal(new Set(all).size, all.length, "no project may run in two workspace lanes");
});
test("shard flags are rejected for the workspaces-c group", () => {
const result = dryRun(["--mode", "general", "--group", "general-workspaces-c", "--shard-index", "0", "--shard-count", "3"]);
assert.notEqual(result.status, 0, "workspaces-c must not accept shard flags");
});
+44 -18
View File
@@ -69,6 +69,9 @@ const generalModeName = "general";
const allModeName = "all";
const generalServerGroupName = "general-server";
const generalServerWithoutChatGroupName = "general-server-without-chat";
// Release verification on master selects this explicit group (release-verify.yml
// after #15619) and runs the native-runner suite in its cached Runner lane.
const generalServerWithoutChatOrNativeRunnerGroupName = "general-server-without-chat-or-native-runner";
const generalChatGroupName = "general-chat";
const generalServerNativeRunnerGroupName = "general-server-native-runner";
const chatSuite = "server/src/__tests__/chat-channels.integration.test.ts";
@@ -92,14 +95,35 @@ const nativeRunnerSuiteRunsInRustCachedLane = process.env.GITHUB_WORKFLOW === pr
const withoutChatExcludedSuites = nativeRunnerSuiteRunsInRustCachedLane
? [chatSuite, nativeRunnerSuite]
: [chatSuite];
const generalServerGroups = [
generalServerGroupName,
generalServerWithoutChatGroupName,
generalServerWithoutChatOrNativeRunnerGroupName,
];
function excludedServerSuites(groupName) {
if (groupName === generalServerWithoutChatOrNativeRunnerGroupName) return [chatSuite, nativeRunnerSuite];
if (groupName === generalServerWithoutChatGroupName) return withoutChatExcludedSuites;
return [];
}
const generalWorkspacesAGroupName = "general-workspaces-a";
const generalWorkspacesBGroupName = "general-workspaces-b";
// The db project runs as its own lane (release-verify.yml after #15831).
const generalWorkspacesCGroupName = "general-workspaces-c";
const generalWorkspacesAProjects = ["@paperclipai/ui", "paperclipai"];
const generalWorkspacesBProjects = nonServerProjects.filter((project) => !generalWorkspacesAProjects.includes(project));
const generalGroupNames = [generalServerGroupName, generalWorkspacesAGroupName, generalWorkspacesBGroupName];
const generalWorkspacesCProjects = ["@paperclipai/db"];
const generalWorkspacesBProjects = nonServerProjects.filter(
(project) => !generalWorkspacesAProjects.includes(project) && !generalWorkspacesCProjects.includes(project),
);
const generalGroupNames = [
generalServerGroupName,
generalWorkspacesAGroupName,
generalWorkspacesBGroupName,
generalWorkspacesCGroupName,
];
const allowedGeneralGroupNames = [
...generalGroupNames,
generalServerWithoutChatGroupName,
generalServerWithoutChatOrNativeRunnerGroupName,
generalChatGroupName,
generalServerNativeRunnerGroupName,
];
@@ -248,7 +272,7 @@ function parseCliOptions(argv) {
const shardAllowed =
mode === serializedModeName ||
(mode === generalModeName &&
([generalServerGroupName, generalServerWithoutChatGroupName, generalChatGroupName, generalWorkspacesAGroupName].includes(group)));
([...generalServerGroups, generalChatGroupName, generalWorkspacesAGroupName].includes(group)));
if (!shardAllowed && shardIndex !== null) {
fail(
"--shard-index/--shard-count are only valid with serialized mode or a shardable general server/chat/workspaces-a group.",
@@ -388,14 +412,13 @@ function runGeneralGroup(routeTests, groupName, shardIndex = null, shardCount =
);
return;
}
if (groupName === generalServerGroupName || groupName === generalServerWithoutChatGroupName) {
if (generalServerGroups.includes(groupName)) {
// In the PR workflow the without-chat group also leaves the native-runner
// suite to the Rust-cached vitest lane; the full general-server group
// (local runs) keeps both.
const withoutChat = groupName === generalServerWithoutChatGroupName;
const files = withoutChat
? generalServerTestFiles.filter((file) => !withoutChatExcludedSuites.includes(file))
: generalServerTestFiles;
// (local runs) keeps both. The explicit without-chat-or-native-runner
// group always excludes both, for callers that run those lanes separately.
const excludedSuites = excludedServerSuites(groupName);
const files = generalServerTestFiles.filter((file) => !excludedSuites.includes(file));
if (shardCount !== null && shardCount > 1) {
const shardFiles = selectGeneralServerShard(
files,
@@ -423,10 +446,8 @@ function runGeneralGroup(routeTests, groupName, shardIndex = null, shardCount =
}
const excludeRouteArgs = routeTests.flatMap((file) => ["--exclude", file.serverPath]);
if (withoutChat) {
for (const suite of withoutChatExcludedSuites) {
excludeRouteArgs.push("--exclude", suite.replace(/^server\//, ""));
}
for (const suite of excludedSuites) {
excludeRouteArgs.push("--exclude", suite.replace(/^server\//, ""));
}
runVitest(
[
@@ -454,6 +475,11 @@ function runGeneralGroup(routeTests, groupName, shardIndex = null, shardCount =
return;
}
if (groupName === generalWorkspacesCGroupName) {
runProjectGroup(generalWorkspacesCProjects, groupName);
return;
}
fail(`Unknown group "${groupName}".`);
}
@@ -519,12 +545,10 @@ if (options.dryRun) {
options.mode === generalModeName && options.group === generalServerNativeRunnerGroupName
? [nativeRunnerSuite]
: options.mode === generalModeName &&
[generalServerGroupName, generalServerWithoutChatGroupName].includes(options.group) &&
generalServerGroups.includes(options.group) &&
options.shardCount !== null
? selectGeneralServerShard(
options.group === generalServerWithoutChatGroupName
? generalServerTestFiles.filter((file) => !withoutChatExcludedSuites.includes(file))
: generalServerTestFiles,
generalServerTestFiles.filter((file) => !excludedServerSuites(options.group).includes(file)),
options.shardIndex,
options.shardCount,
generalServerShardDurations,
@@ -535,7 +559,9 @@ if (options.dryRun) {
? generalWorkspacesAProjects
: options.group === generalWorkspacesBGroupName
? generalWorkspacesBProjects
: null,
: options.group === generalWorkspacesCGroupName
? generalWorkspacesCProjects
: null,
workspacesVitestShard:
options.group === generalWorkspacesAGroupName &&
options.shardCount !== null &&