From 7d67fc4303807640a6a402806c7848e95c1ea9c2 Mon Sep 17 00:00:00 2001 From: zhyongrui Date: Wed, 11 Mar 2026 14:57:21 +0000 Subject: [PATCH] feat: implement issue #40 --- src/commands/openclawcode.test.ts | 33 +++++++++++++++++++++++++++++++ src/commands/openclawcode.ts | 20 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/commands/openclawcode.test.ts b/src/commands/openclawcode.test.ts index 54f91c7883..a3acaddb94 100644 --- a/src/commands/openclawcode.test.ts +++ b/src/commands/openclawcode.test.ts @@ -301,6 +301,39 @@ describe("openclawCodeRunCommand", () => { expect(payload.verificationFollowUpCount).toBe(2); }); + it("prints docs-only webhook smoke test signals derived from the issue run", async () => { + mocks.runIssueWorkflow.mockResolvedValue( + createRun({ + issue: { + owner: "openclaw", + repo: "openclaw", + number: 40, + title: "[Feature]: Webhook auto-intake smoke test after public tunnel setup", + }, + buildResult: { + branchName: "openclawcode/issue-40", + summary: "Create a tiny docs-only smoke test issue for webhook tunnel validation.", + changedFiles: ["docs/openclawcode/webhook-smoke-test.md"], + issueClassification: "command-layer", + scopeCheck: { + ok: true, + blockedFiles: [], + summary: "Scope check passed for command-layer issue.", + }, + testCommands: [], + testResults: [], + notes: [], + }, + }), + ); + + await openclawCodeRunCommand({ issue: "40", repoRoot: "/repo", json: true }, runtime); + + const payload = JSON.parse(runtime.log.mock.calls[0]?.[0] ?? "null"); + expect(payload.issueDocsOnly).toBe(true); + expect(payload.issueWebhookSmokeTest).toBe(true); + }); + it("prints failed auto-merge disposition when merge execution fails", async () => { mocks.runIssueWorkflow.mockResolvedValue( createRun({ diff --git a/src/commands/openclawcode.ts b/src/commands/openclawcode.ts index 03a3443088..444e424381 100644 --- a/src/commands/openclawcode.ts +++ b/src/commands/openclawcode.ts @@ -237,6 +237,24 @@ function resolveVerificationApprovedForHumanReview(run: WorkflowRun): boolean | return decision === "approve-for-human-review"; } +function resolveIssueDocsOnly(run: WorkflowRun): boolean { + const changedFiles = run.buildResult?.changedFiles; + if (!changedFiles || changedFiles.length === 0) { + return false; + } + + return changedFiles.every((file) => file.startsWith("docs/")); +} + +function resolveIssueWebhookSmokeTest(run: WorkflowRun): boolean { + const content = [run.issue.title, run.buildResult?.summary] + .filter((value): value is string => Boolean(value)) + .join(" ") + .toLowerCase(); + + return content.includes("webhook") && content.includes("smoke test"); +} + function toWorkflowRunJson(run: WorkflowRun) { const autoMergePolicy = resolveAutoMergePolicy(run); const autoMergeDisposition = resolveAutoMergeDisposition(run); @@ -251,6 +269,8 @@ function toWorkflowRunJson(run: WorkflowRun) { changeDisposition: changeDisposition.changeDisposition, changeDispositionReason: changeDisposition.changeDispositionReason, issueClassification: run.buildResult?.issueClassification ?? null, + issueDocsOnly: resolveIssueDocsOnly(run), + issueWebhookSmokeTest: resolveIssueWebhookSmokeTest(run), scopeCheck: run.buildResult?.scopeCheck ?? null, scopeCheckSummary: run.buildResult?.scopeCheck?.summary ?? null, scopeCheckPassed: run.buildResult?.scopeCheck?.ok ?? null,