Skip unrelated required checks without losing their names (#2542)
* fix(ci): skip unrelated required checks cleanly Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable. * fix(ci): gate changes to install patching script * fix(ci): route cross-package test contracts narrowly * fix(ci): keep routed contracts merge-blocking * fix(server): exclude nested test utilities from builds * fix(ci): cover app-owned native and browser domains * refactor(ci): align tests with runtime domains Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check. * fix(ci): keep routing and desktop suites isolated The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs. * fix(ci): isolate desktop e2e runtime * perf(test): isolate Playwright workers * perf(test): parallelize Playwright within specs * fix(test): keep Playwright file scheduling * fix(test): share dynamic Metro fixture * fix(test): preserve own-host restart fixtures * fix(ci): preserve routed contracts after rebase * Stabilize Playwright wheel-scroll assertions
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AgentSnapshotPayloadSchema,
|
||||
AgentTimelineItemPayloadSchema,
|
||||
ServerInfoStatusPayloadSchema,
|
||||
WSHelloMessageSchema,
|
||||
} from "./messages.js";
|
||||
|
||||
const LegacySubAgentToolCallSchema = z.object({
|
||||
type: z.literal("tool_call"),
|
||||
callId: z.string(),
|
||||
name: z.string(),
|
||||
status: z.enum(["running", "completed", "failed", "canceled"]),
|
||||
error: z.unknown().nullable(),
|
||||
detail: z.object({
|
||||
type: z.literal("sub_agent"),
|
||||
subAgentType: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
log: z.string(),
|
||||
// Copied from v0.1.65-beta.3: actions was required even though the UI ignored it.
|
||||
actions: z.array(
|
||||
z.object({
|
||||
index: z.number().int().positive(),
|
||||
toolName: z.string(),
|
||||
summary: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const LegacyAgentCapabilityFlagsSchema = z.object({
|
||||
supportsStreaming: z.boolean(),
|
||||
supportsSessionPersistence: z.boolean(),
|
||||
supportsDynamicModes: z.boolean(),
|
||||
supportsMcpServers: z.boolean(),
|
||||
supportsReasoningStream: z.boolean(),
|
||||
supportsToolInvocations: z.boolean(),
|
||||
});
|
||||
|
||||
const LegacyAgentSnapshotPayloadSchema = AgentSnapshotPayloadSchema.extend({
|
||||
capabilities: LegacyAgentCapabilityFlagsSchema,
|
||||
});
|
||||
|
||||
describe("wire schema compatibility", () => {
|
||||
test("hello parses with and without the project update capability", () => {
|
||||
const legacy = WSHelloMessageSchema.parse({
|
||||
type: "hello",
|
||||
clientId: "legacy-client",
|
||||
clientType: "mobile",
|
||||
protocolVersion: 1,
|
||||
});
|
||||
const capable = WSHelloMessageSchema.parse({
|
||||
type: "hello",
|
||||
clientId: "capable-client",
|
||||
clientType: "mobile",
|
||||
protocolVersion: 1,
|
||||
capabilities: { project_updates: true },
|
||||
});
|
||||
|
||||
expect([legacy, capable]).toEqual([
|
||||
{
|
||||
type: "hello",
|
||||
clientId: "legacy-client",
|
||||
clientType: "mobile",
|
||||
protocolVersion: 1,
|
||||
},
|
||||
{
|
||||
type: "hello",
|
||||
clientId: "capable-client",
|
||||
clientType: "mobile",
|
||||
protocolVersion: 1,
|
||||
capabilities: { project_updates: true },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("server info strips unknown legacy features while accepting former turn identity", () => {
|
||||
const parsed = ServerInfoStatusPayloadSchema.parse({
|
||||
status: "server_info",
|
||||
serverId: "legacy-server",
|
||||
features: {
|
||||
workspaceGithubClone: true,
|
||||
agentTurnIdentity: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(parsed).toEqual({
|
||||
status: "server_info",
|
||||
serverId: "legacy-server",
|
||||
hostname: null,
|
||||
version: null,
|
||||
features: { agentTurnIdentity: true },
|
||||
});
|
||||
});
|
||||
|
||||
test("assistant timeline message ids are optional on the wire", () => {
|
||||
expect(
|
||||
AgentTimelineItemPayloadSchema.parse({
|
||||
type: "assistant_message",
|
||||
text: "old daemon shape",
|
||||
}),
|
||||
).toEqual({
|
||||
type: "assistant_message",
|
||||
text: "old daemon shape",
|
||||
});
|
||||
expect(
|
||||
AgentTimelineItemPayloadSchema.parse({
|
||||
type: "assistant_message",
|
||||
text: "new daemon shape",
|
||||
messageId: "msg-1",
|
||||
}),
|
||||
).toEqual({
|
||||
type: "assistant_message",
|
||||
text: "new daemon shape",
|
||||
messageId: "msg-1",
|
||||
});
|
||||
});
|
||||
|
||||
test("sub_agent tool-call payload still parses against the v0.1.65-beta.3 schema", () => {
|
||||
const parsed = LegacySubAgentToolCallSchema.parse({
|
||||
type: "tool_call",
|
||||
callId: "call-sub-agent-1",
|
||||
name: "Task",
|
||||
status: "completed",
|
||||
error: null,
|
||||
detail: {
|
||||
type: "sub_agent",
|
||||
subAgentType: "Explore",
|
||||
description: "Inspect repository structure",
|
||||
childSessionId: "child-session-1",
|
||||
log: "[Read] README.md",
|
||||
actions: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(parsed.detail.actions).toEqual([]);
|
||||
});
|
||||
|
||||
test("old clients parse agent snapshots with rewind capabilities", () => {
|
||||
const parsed = LegacyAgentSnapshotPayloadSchema.parse({
|
||||
id: "agent-1",
|
||||
provider: "claude",
|
||||
cwd: "/tmp/project",
|
||||
model: null,
|
||||
thinkingOptionId: null,
|
||||
effectiveThinkingOptionId: null,
|
||||
createdAt: "2026-05-23T00:00:00.000Z",
|
||||
updatedAt: "2026-05-23T00:00:00.000Z",
|
||||
lastUserMessageAt: null,
|
||||
status: "idle",
|
||||
capabilities: {
|
||||
supportsStreaming: true,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: true,
|
||||
supportsMcpServers: true,
|
||||
supportsReasoningStream: true,
|
||||
supportsToolInvocations: true,
|
||||
supportsRewindConversation: true,
|
||||
supportsRewindFiles: true,
|
||||
supportsRewindBoth: true,
|
||||
},
|
||||
currentModeId: null,
|
||||
availableModes: [],
|
||||
pendingPermissions: [],
|
||||
persistence: null,
|
||||
title: null,
|
||||
labels: {},
|
||||
});
|
||||
|
||||
expect(parsed.capabilities).toEqual({
|
||||
supportsStreaming: true,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: true,
|
||||
supportsMcpServers: true,
|
||||
supportsReasoningStream: true,
|
||||
supportsToolInvocations: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("new clients parse agent snapshots without rewind capabilities", () => {
|
||||
const parsed = AgentSnapshotPayloadSchema.parse({
|
||||
id: "agent-1",
|
||||
provider: "claude",
|
||||
cwd: "/tmp/project",
|
||||
model: null,
|
||||
thinkingOptionId: null,
|
||||
effectiveThinkingOptionId: null,
|
||||
createdAt: "2026-05-23T00:00:00.000Z",
|
||||
updatedAt: "2026-05-23T00:00:00.000Z",
|
||||
lastUserMessageAt: null,
|
||||
status: "idle",
|
||||
capabilities: {
|
||||
supportsStreaming: true,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: true,
|
||||
supportsMcpServers: true,
|
||||
supportsReasoningStream: true,
|
||||
supportsToolInvocations: true,
|
||||
},
|
||||
currentModeId: null,
|
||||
availableModes: [],
|
||||
pendingPermissions: [],
|
||||
persistence: null,
|
||||
title: null,
|
||||
labels: {},
|
||||
});
|
||||
|
||||
expect(parsed.capabilities.supportsRewindConversation).toBe(false);
|
||||
expect(parsed.capabilities.supportsRewindFiles).toBe(false);
|
||||
expect(parsed.capabilities.supportsRewindBoth).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user