

























@@ -44,14 +44,20 @@ function file(filename: string, status = "modified") {
4444};
4545}
464647-function barnacleContext(pullRequest: Record<string, unknown>, labels: string[] = []) {
47+function barnacleContext(
48+pullRequest: Record<string, unknown>,
49+labels: string[] = [],
50+options: Record<string, unknown> = {},
51+) {
4852return {
4953repo: {
5054owner: "openclaw",
5155repo: "openclaw",
5256},
5357payload: {
54-action: "opened",
58+action: options.action ?? "opened",
59+label: options.label,
60+sender: options.sender,
5561pull_request: {
5662number: 123,
5763title: "Cleanup plugin docs",
@@ -70,7 +76,10 @@ function barnacleContext(pullRequest: Record<string, unknown>, labels: string[]
7076function barnacleGithub(files: ReturnType<typeof file>[]) {
7177const calls = {
7278addLabels: [] as Array<{ issue_number: number; labels: string[] }>,
79+createComment: [] as Array<{ issue_number: number; body: string }>,
80+lock: [] as Array<{ issue_number: number; lock_reason?: string }>,
7381removeLabel: [] as Array<{ issue_number: number; name: string }>,
82+update: [] as Array<{ issue_number: number; state?: string }>,
7483};
7584const github = {
7685paginate: async () => files,
@@ -79,7 +88,9 @@ function barnacleGithub(files: ReturnType<typeof file>[]) {
7988addLabels: async (params: { issue_number: number; labels: string[] }) => {
8089calls.addLabels.push(params);
8190},
82-createComment: async () => undefined,
91+createComment: async (params: { issue_number: number; body: string }) => {
92+calls.createComment.push(params);
93+},
8394createLabel: async () => undefined,
8495getLabel: async (params: { name: string }) => ({
8596data: {
@@ -89,11 +100,15 @@ function barnacleGithub(files: ReturnType<typeof file>[]) {
89100managedLabelSpecs[params.name as keyof typeof managedLabelSpecs]?.description ?? "",
90101},
91102}),
92-lock: async () => undefined,
103+lock: async (params: { issue_number: number; lock_reason?: string }) => {
104+calls.lock.push(params);
105+},
93106removeLabel: async (params: { issue_number: number; name: string }) => {
94107calls.removeLabel.push(params);
95108},
96-update: async () => undefined,
109+update: async (params: { issue_number: number; state?: string }) => {
110+calls.update.push(params);
111+},
97112updateLabel: async () => undefined,
98113},
99114pulls: {
@@ -195,6 +210,30 @@ describe("barnacle-auto-response", () => {
195210expect(labels).not.toContain(candidateLabels.dirtyCandidate);
196211});
197212213+it("does not classify a linked core plugin auto-enable fix as an external plugin candidate", () => {
214+const labels = classifyPullRequestCandidateLabels(
215+pr(
216+"Fix duplicate plugin auto-enable entries",
217+[
218+"- Problem: openclaw doctor --fix adds duplicate installed plugin entries",
219+"- Why it matters: users get noisy config churn",
220+"- What changed: respect manifest-provided channel auto-loads",
221+"",
222+"Fixes #37548",
223+"",
224+"This touches external plugin install state but fixes core config repair behavior.",
225+].join("\n"),
226+),
227+[
228+file("src/config/plugin-auto-enable.shared.ts"),
229+file("src/config/plugin-auto-enable.channels.test.ts"),
230+file("src/config/plugin-auto-enable.test-helpers.ts"),
231+],
232+);
233+234+expect(labels).not.toContain(candidateLabels.externalPluginCandidate);
235+});
236+198237it("does not add candidate labels to maintainer-authored PRs", async () => {
199238const { calls, github } = barnacleGithub([
200239file("ui/src/app.ts"),
@@ -272,5 +311,73 @@ describe("barnacle-auto-response", () => {
272311labels: expect.arrayContaining([candidateLabels.dirtyCandidate]),
273312}),
274313);
314+expect(calls.createComment).toEqual([]);
315+expect(calls.update).toEqual([]);
316+});
317+318+it("actions manually applied candidate labels", async () => {
319+const { calls, github } = barnacleGithub([file("extensions/example/openclaw.plugin.json")]);
320+321+await runBarnacleAutoResponse({
322+ github,
323+context: barnacleContext({}, [candidateLabels.externalPluginCandidate], {
324+action: "labeled",
325+label: { name: candidateLabels.externalPluginCandidate },
326+sender: { login: "maintainer", type: "User" },
327+}),
328+core: {
329+info: () => undefined,
330+},
331+});
332+333+expect(calls.createComment).toContainEqual(
334+expect.objectContaining({
335+body: expect.stringContaining("ClawHub"),
336+}),
337+);
338+expect(calls.update).toContainEqual(expect.objectContaining({ state: "closed" }));
339+});
340+341+it("keeps bot-applied candidate labels passive", async () => {
342+const { calls, github } = barnacleGithub([file("extensions/example/openclaw.plugin.json")]);
343+344+await runBarnacleAutoResponse({
345+ github,
346+context: barnacleContext({}, [candidateLabels.externalPluginCandidate], {
347+action: "labeled",
348+label: { name: candidateLabels.externalPluginCandidate },
349+sender: { login: "openclaw-bot[bot]", type: "Bot" },
350+}),
351+core: {
352+info: () => undefined,
353+},
354+});
355+356+expect(calls.createComment).toEqual([]);
357+expect(calls.update).toEqual([]);
358+});
359+360+it("actions existing candidate labels when a maintainer adds trigger-response", async () => {
361+const { calls, github } = barnacleGithub([file("src/gateway/foo.test.ts")]);
362+363+await runBarnacleAutoResponse({
364+ github,
365+context: barnacleContext({}, [candidateLabels.testOnlyNoBug, "trigger-response"], {
366+action: "labeled",
367+label: { name: "trigger-response" },
368+sender: { login: "maintainer", type: "User" },
369+}),
370+core: {
371+info: () => undefined,
372+},
373+});
374+375+expect(calls.removeLabel).toContainEqual(expect.objectContaining({ name: "trigger-response" }));
376+expect(calls.createComment).toContainEqual(
377+expect.objectContaining({
378+body: expect.stringContaining("only changes tests"),
379+}),
380+);
381+expect(calls.update).toContainEqual(expect.objectContaining({ state: "closed" }));
275382});
276383});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。