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
10 changes: 7 additions & 3 deletions test/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,13 +616,17 @@ controller completes it as cancelled with `Superseded by PR update` or
`PR closed — gate no longer applies` and identifies the obsolete head and base.
The closed-PR outcome also applies when a fork repository was deleted and
GitHub consequently returns no head-repository object.
Shared sandbox-boundary changes have a floor of `full-e2e`, `hermes-e2e`, and
`security-posture`. E2E control-plane changes select `cloud-onboard`,
`cloud-inference`, and `security-posture`. The `e2e-control-plane`
Shared sandbox-boundary changes have a floor of `full-e2e`, `hermes-e2e`,
`hermes-inference-switch`, and `security-posture`. E2E control-plane changes
select `cloud-onboard`, `cloud-inference`, and `security-posture`. The `e2e-control-plane`
family remains the conservative boundary for shared E2E tools, workflow and
security files, unknown live test paths, risk policy, dependency and test
configuration, and preparation and upload actions. These cross-cutting changes
keep the broad three-job floor.
Changes to the Hermes CLI wrapper, adapter manifest, or adapter validator also
select `channels-stop-start` and `mcp-bridge`. Both jobs include the Hermes
shard. The Hermes shards exercise the wrapper during `channels stop` and
`channels start`, and the adapter during `mcp add`, `mcp restart`, and `mcp remove`.
Repository-root `Dockerfile` changes additionally select `full-e2e` alongside
the platform-install `cloud-onboard` floor so OpenClaw final-image changes run
through cold onboarding and a real first turn.
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/support/e2e-cross-runtime-compatibility.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("cross-runtime foundation compatibility", () => {
];

expect(digestOutput(cases.map(buildRiskPlan))).toBe(
"80ba8dfc73d0c7568d77338f0061a205a4414858749b33a7a7af6b4b670be8d7",
"b9f2f00f87b7a18caac504048c72eea224c10621690d2cf66549733487d3d342",
);
});
});
100 changes: 90 additions & 10 deletions test/pr-risk-plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import {
import { classifyTestDepth } from "../tools/pr-review-advisor/analyze.mts";

const HEAD_SHA = "a".repeat(40);
const HERMES_SANDBOX_BOUNDARY_JOBS = [
"full-e2e",
"hermes-e2e",
"hermes-inference-switch",
"security-posture",
];
const HERMES_CLI_ADAPTER_JOBS = ["channels-stop-start", "mcp-bridge"];
const HERMES_MANAGED_POLICY_JOBS = [
"bedrock-runtime-compatible-anthropic",
"channels-stop-start",
Expand All @@ -25,6 +32,28 @@ const HERMES_MANAGED_POLICY_JOBS = [
"hermes-shields-config",
"security-posture",
];
const HERMES_CLI_ADAPTER_REQUIRED_JOBS = [
...HERMES_SANDBOX_BOUNDARY_JOBS,
...HERMES_CLI_ADAPTER_JOBS,
];
const HERMES_MANAGED_POLICY_REQUIRED_JOBS = [
...HERMES_SANDBOX_BOUNDARY_JOBS,
"bedrock-runtime-compatible-anthropic",
"channels-stop-start",
"dashboard-remote-bind",
"hermes-shields-config",
];
const HERMES_WRAPPER_FOCUSED_JOBS = [
"bedrock-runtime-compatible-anthropic",
"channels-stop-start",
"dashboard-remote-bind",
"hermes-e2e",
"hermes-inference-switch",
"hermes-shields-config",
"mcp-bridge",
"security-posture",
];
const HERMES_WRAPPER_REQUIRED_JOBS = [...HERMES_MANAGED_POLICY_REQUIRED_JOBS, "mcp-bridge"];
const HERMES_MANAGED_POLICY_FILES = [
"agents/hermes/config/managed-policy.ts",
"agents/hermes/hermes-wrapper.py",
Expand All @@ -46,7 +75,7 @@ describe("deterministic PR risk plan", () => {
const second = plan("src/lib/onboard.ts", "src/lib/state/registry.ts");

expect(first).toEqual(second);
expect(first.version).toBe(11);
expect(first.version).toBe(12);
expect(first.headSha).toBe(HEAD_SHA);
expect(first.planHash).toMatch(/^[a-f0-9]{64}$/u);
expect(first.changedFiles).toEqual(["src/lib/onboard.ts", "src/lib/state/registry.ts"]);
Expand Down Expand Up @@ -136,30 +165,76 @@ describe("deterministic PR risk plan", () => {
]);
});

it.each([
"agents/hermes/hermes-cli-adapter-v1.json",
"agents/hermes/hermes-wrapper.py",
"agents/hermes/validate-cli-adapter.py",
])("selects Hermes MCP and channel lifecycle E2E for %s (#8011)", (changedFile) => {
const result = plan(changedFile);
const isWrapper = changedFile === "agents/hermes/hermes-wrapper.py";
const expectedFocusedJobs = isWrapper ? HERMES_WRAPPER_FOCUSED_JOBS : HERMES_CLI_ADAPTER_JOBS;
const expectedRequiredJobs = isWrapper
? HERMES_WRAPPER_REQUIRED_JOBS
: HERMES_CLI_ADAPTER_REQUIRED_JOBS;

const focusedFamily = result.families.find((family) => family.id === "focused-e2e");
expect(focusedFamily).toEqual(
expect.objectContaining({
matchedFiles: [changedFile],
requiredJobs: expectedFocusedJobs,
}),
);
expect(riskPlanRequiredJobIds(result)).toEqual(expectedRequiredJobs);
});

it.each(
HERMES_MANAGED_POLICY_FILES,
)("selects every Hermes managed-policy live E2E job for %s (#8008)", (changedFile) => {
const result = plan(changedFile);

expect(result.families).toContainEqual(
const isWrapper = changedFile === "agents/hermes/hermes-wrapper.py";
const expectedFocusedJobs = isWrapper
? HERMES_WRAPPER_FOCUSED_JOBS
: HERMES_MANAGED_POLICY_JOBS;
const expectedRequiredJobs = isWrapper
? HERMES_WRAPPER_REQUIRED_JOBS
: changedFile === "src/lib/hermes-managed-route.ts"
? HERMES_MANAGED_POLICY_JOBS
: HERMES_MANAGED_POLICY_REQUIRED_JOBS;

const focusedFamily = result.families.find((family) => family.id === "focused-e2e");
expect(focusedFamily).toEqual(
expect.objectContaining({
id: "focused-e2e",
matchedFiles: [changedFile],
requiredJobs: HERMES_MANAGED_POLICY_JOBS,
requiredJobs: expectedFocusedJobs,
}),
);
expect(riskPlanRequiredJobIds(result)).toEqual(
expect.arrayContaining(HERMES_MANAGED_POLICY_JOBS),
);
expect(riskPlanRequiredJobIds(result)).toEqual(expectedRequiredJobs);
});

it("does not select managed-policy E2E for an unrelated Hermes runtime file (#8008)", () => {
const result = plan("agents/hermes/runtime-version.py");

expect(result.families).not.toContainEqual(expect.objectContaining({ id: "focused-e2e" }));
expect(riskPlanRequiredJobIds(result)).toEqual(["full-e2e", "hermes-e2e", "security-posture"]);
expect(riskPlanRequiredJobIds(result)).toEqual([
"full-e2e",
"hermes-e2e",
"hermes-inference-switch",
"security-posture",
]);
});

it("combines CLI adapter and managed-policy E2E for the Hermes wrapper (#8011)", () => {
const result = plan("agents/hermes/hermes-wrapper.py");

expect(result.families).toContainEqual(
expect.objectContaining({
id: "focused-e2e",
matchedFiles: ["agents/hermes/hermes-wrapper.py"],
requiredJobs: HERMES_WRAPPER_FOCUSED_JOBS,
}),
);
expect(riskPlanRequiredJobIds(result)).toEqual(HERMES_WRAPPER_REQUIRED_JOBS);
});
it("leaves E2E support-only changes in the fast e2e-support project (#7921)", () => {
const changedFiles = ["test/e2e/support/workflow-plan.test.ts"];
const focusedE2eJobs = focusedE2eJobsForChangedFiles(changedFiles);
Expand Down Expand Up @@ -543,7 +618,12 @@ describe("deterministic PR risk plan", () => {

expect(result.families.map((family) => family.id)).toContain("sandbox-boundary");
expect(riskPlanRequiredJobIds(result)).toEqual(
expect.arrayContaining(["full-e2e", "hermes-e2e", "security-posture"]),
expect.arrayContaining([
"full-e2e",
"hermes-e2e",
"hermes-inference-switch",
"security-posture",
]),
);
});

Expand Down
20 changes: 18 additions & 2 deletions tools/advisors/risk-plan.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { createHash } from "node:crypto";

export const RISK_PLAN_VERSION = 11 as const;
export const RISK_PLAN_VERSION = 12 as const;

export const PR_E2E_TYPED_TARGET_IDS = [
"ubuntu-repo-cloud-langchain-deepagents-code",
Expand All @@ -25,6 +25,12 @@ const MANAGED_STARTUP_E2E_JOB_IDS = [
"issue-4462-scope-upgrade-approval",
"openclaw-inference-switch",
] as const;
const HERMES_CLI_ADAPTER_E2E_JOB_IDS = ["channels-stop-start", "mcp-bridge"] as const;
const HERMES_CLI_ADAPTER_RUNTIME_FILES = new Set([
"agents/hermes/hermes-cli-adapter-v1.json",
"agents/hermes/hermes-wrapper.py",
"agents/hermes/validate-cli-adapter.py",
]);
const HERMES_MANAGED_POLICY_E2E_JOB_IDS = [
"bedrock-runtime-compatible-anthropic",
"channels-stop-start",
Expand Down Expand Up @@ -192,6 +198,11 @@ export function focusedPrE2eJobsForChangedFiles(
isRuntimeRelevant(file),
),
);
const hermesCliAdapterFiles = stableUnique(
changedFiles.filter(
(file) => HERMES_CLI_ADAPTER_RUNTIME_FILES.has(file) && isRuntimeRelevant(file),
),
);
const hermesManagedPolicyFiles = stableUnique(
changedFiles.filter(
(file) =>
Expand All @@ -201,6 +212,10 @@ export function focusedPrE2eJobsForChangedFiles(
);
return [
...MANAGED_STARTUP_E2E_JOB_IDS.map((id) => ({ id, matchedFiles: managedStartupFiles })),
...HERMES_CLI_ADAPTER_E2E_JOB_IDS.map((id) => ({
id,
matchedFiles: hermesCliAdapterFiles,
})),
...HERMES_MANAGED_POLICY_E2E_JOB_IDS.map((id) => ({
id,
matchedFiles: hermesManagedPolicyFiles,
Expand Down Expand Up @@ -372,9 +387,10 @@ export const RISK_RULES: readonly RiskRule[] = [
summary:
"Sandbox blueprint and agent-runtime changes must preserve equivalent isolation and readiness across supported agents.",
tier: 3,
requiredJobs: ["full-e2e", "hermes-e2e", "security-posture"],
requiredJobs: ["full-e2e", "hermes-e2e", "hermes-inference-switch", "security-posture"],
invariants: [
"OpenClaw and Hermes both reach readiness through the changed sandbox boundary",
"the Hermes runtime and managed inference route agree on the selected provider and model after each route change",
"the sandbox retains its required security posture and isolation controls",
"blueprint state agrees with the runtime observed by both supported agents",
],
Expand Down
13 changes: 12 additions & 1 deletion tools/pr-review-advisor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Authors and coding agents should follow the shared [PR CI and Review Follow-Up](
job that the model omits or downgrades. The PR E2E controller separately dispatches every listed
job without consuming the advisor's normalized result.

Risk plan version 10 maps runtime changes from these paths to the `focused-e2e` family:
Risk plan version 12 maps runtime changes from these paths to the `focused-e2e` family:

- `src/lib/onboard/managed-startup/**`.
- `src/lib/onboard/sandbox-create-launch.ts`.
Expand All @@ -124,6 +124,17 @@ Each match selects these focused E2E jobs:
- `issue-4462-scope-upgrade-approval`.
- `openclaw-inference-switch`.

The same risk plan maps these Hermes CLI adapter paths to `focused-e2e`:

- `agents/hermes/hermes-cli-adapter-v1.json`.
- `agents/hermes/hermes-wrapper.py`.
- `agents/hermes/validate-cli-adapter.py`.

Each Hermes CLI adapter match selects these focused E2E jobs:

- `channels-stop-start`.
- `mcp-bridge`.

## Required secret

Configure this repository secret for review analysis:
Expand Down
Loading