Skip to content
Draft
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
1 change: 1 addition & 0 deletions prompts/review-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ Close only when the evidence is strong and the repository policy allows it. Allo
- `mostly_implemented_on_main`: an older PR is more than 60 days old, current `main` already implements the central useful part of the PR, and no meaningful unique remainder should be merged from the branch. Use only for pull requests, not issues. The close comment must say what part is already on `main`, what leftover part is minor/obsolete/superseded or separately tracked, and why keeping the stale branch open is not useful.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not current mani report

- `cannot_reproduce`: you tried a reasonable reproduction path against current `main` and it does not reproduce, or the report is obsolete and no longer matches current behavior.
- `clawhub`: useful idea, but it belongs as a ClawHub skill/plugin rather than OpenClaw core. Use `VISION.md` as the scope anchor. Prefer this when the requested capability is optional integration/provider/channel/skill/bundle/MCP work, can be built with current skill/MCP/plugin surfaces, has no concrete missing core extension API, and has no protected maintainer signal. This includes service-specific channels, providers, optional skills, and plugin-discovery/publishing ideas when the current plugin or bundle-style interface is sufficient. For OpenClaw PRs that only add bundled skills under paths like `skills/<vendor>/**`, set `itemCategory: "skill"` and prefer `closeReason: "clawhub"` with high confidence; the close comment should ask the contributor to upload or publish it through ClawHub.com instead of bundling it in OpenClaw core. Keep open when the item reports a regression in bundled core behavior, identifies a missing plugin API needed before external implementation is possible, involves security/core hardening, or clearly needs explicit maintainer product judgment.
For `clawhub` closes, make `bestSolution` and `closeComment` useful as a self-serve handoff: identify the likely package shape such as skill, plugin, provider, channel, bundle, or MCP integration; list manifest/package basics to verify, such as metadata, entrypoint, permissions, secrets/config, install/update docs, example usage, and smoke proof; and explicitly state that ClawSweeper should not open a ClawHub issue, open a ClawHub PR, create a tracking issue, or publish the package on the contributor's behalf.
- `duplicate_or_superseded`: another issue/PR already tracks the same remaining work, or the linked discussion/PR clearly supersedes this item. Link the canonical item and explain whether it is open or closed/merged. For clusters with the same root cause, keep one canonical issue open and close satellites when their unique logs, platforms, or context can be preserved by linking them in the close comment. For PR-to-PR supersession, the canonical PR must be merged or still open, proof-positive, and viable; do not treat a closed-unmerged, F/no-proof, proposed-close, not-cleanly-mergeable, or otherwise unsafe PR as a reason to close another PR. Unique evidence blocks duplicate close only when it implies a distinct root cause, platform-specific fix, or separate remaining product behavior.
- `low_signal_unmergeable_pr`: a pull request may contain a small useful idea, but the submitted branch is net-negative and should not stay open as a landing candidate because most of the diff is unrelated, copied, generated, bloated, internally incoherent, or conflicts with the repository's existing structure. Use this for PRs like a narrow docs title that inserts a large unrelated reference block, a tiny bug fix mixed with broad unrelated rewrites, or generated/vendor/config churn unrelated to the stated purpose. The close comment must acknowledge any useful part, explain the concrete unmergeable diff, and invite a new narrow PR for the useful change. Do not use this when the PR has meaningful unique work that can be repaired without throwing away most of the branch, when maintainers asked to preserve/adopt the branch, when a protected label or maintainer author requires human judgment, or when the only issue is ordinary missing proof, test coverage, style, or review follow-up.
- `unconfirmed_product_direction`: a non-maintainer pull request is technically correct and well-proven, but adds feature or configuration surface without maintainer-confirmed product direction. Use this only when every condition is true: `itemCategory` is `feature`; `requiresProductDecision` is true; at least one of `requiresNewFeature` or `requiresNewConfigOption` is true; `overallCorrectness` is `patch is correct`; there are no review findings; `securityReview.status` is `cleared` with no concerns; real behavior proof is sufficient or overridden; PR quality is C or better; and no `clawsweeper:human-review`, `clawsweeper:manual-only`, `clawsweeper:autofix`, or `clawsweeper:automerge` label is present. Do not use this for maintainer-authored PRs, bugs with an established current behavior contract, security-sensitive work, broken or low-quality patches, or work already calibrated by maintainer discussion. Explain that implementation quality is separate from product acceptance and that a maintainer can sponsor, narrow, or reopen the direction. Apply remains behind a separate default-off policy gate and live maintainer-signal checks.
Expand Down
15 changes: 14 additions & 1 deletion src/clawsweeper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8670,7 +8670,7 @@ function closeOutro(reason: CloseReason, canonicalLinks: string[] = []): string
case "mostly_implemented_on_main":
return "So I’m closing this older PR as already covered on `main` rather than keeping a mostly-duplicated branch open.";
case "clawhub":
return `So I’m closing this as a scope-fit item for the plugin/community path. Please upload or publish it through ${markdownLink("ClawHub.com", targetProfile().communityUrl ?? "https://clawhub.ai/")} so it can live as an installable community skill instead of a bundled OpenClaw core change.`;
return `So I’m closing this as a scope-fit item for the plugin/community path. Please upload or publish it through ${markdownLink("ClawHub.com", targetProfile().communityUrl ?? "https://clawhub.ai/")} so it can live as an installable ClawHub package instead of a bundled OpenClaw core change.`;
case "duplicate_or_superseded":
return canonicalLinks.length
? `So I’m closing this here and keeping the remaining discussion on ${formatCanonicalLinks(canonicalLinks)}.`
Expand All @@ -8686,6 +8686,17 @@ function closeOutro(reason: CloseReason, canonicalLinks: string[] = []): string
}
}

function closeClawHubHandoffBlock(reason: CloseReason): string {
if (reason !== "clawhub") return "";
return [
"If you want to carry this forward, package it as a self-serve ClawHub item rather than a core patch:",
"",
"- Scope: choose the smallest skill, plugin, provider, channel, bundle, or MCP integration that matches the requested capability.",
"- Checklist: include package metadata/manifest, entrypoint, required permissions, secrets/config notes, install/update docs, example usage, and a smoke test or proof command.",
"- Boundary: ClawSweeper will not open a ClawHub issue or PR, create a tracking issue, or publish the package automatically; the contributor should create that ClawHub work separately.",
].join("\n");
}

function issueOrPullReferenceNumbers(value: string): string[] {
return [
...value.matchAll(
Expand Down Expand Up @@ -13797,6 +13808,8 @@ function renderCloseComment(options: {
if (evidence.length) details.push("", "What I checked:", "", ...evidence);
if (likelyOwners.length) details.push("", "Likely related people:", "", ...likelyOwners);

const clawhubHandoff = closeClawHubHandoffBlock(options.reason);
if (clawhubHandoff) lines.push("", "**ClawHub handoff**", clawhubHandoff);
const outro = closeOutro(options.reason, canonicalLinks);
if (outro) lines.push("", outro);
if (options.reviewLine) details.push("", options.reviewLine);
Expand Down
5 changes: 4 additions & 1 deletion test/clawsweeper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,10 @@ test("skill-only OpenClaw PRs can close through ClawHub with upload guidance", (
assert.equal(action.actionTaken, "proposed_close");
assert.match(action.closeComment, /ClawHub\.com/);
assert.match(action.closeComment, /upload or publish/i);
assert.match(action.closeComment, /installable community skill/);
assert.match(action.closeComment, /ClawHub handoff/);
assert.match(action.closeComment, /skill, plugin, provider, channel, bundle, or MCP integration/);
assert.match(action.closeComment, /package metadata\/manifest/);
assert.match(action.closeComment, /will not open a ClawHub issue or PR/);
});

test("ClawHub policy allows main-implemented issue and PR close proposals", () => {
Expand Down
12 changes: 12 additions & 0 deletions test/review-prompt-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ test("review prompt treats plugin API changes as compatibility-sensitive P1 repa
);
});

test("review prompt makes ClawHub closes a self-serve handoff", () => {
const prompt = readFileSync("prompts/review-item.md", "utf8");

assert.match(prompt, /For `clawhub` closes/);
assert.match(prompt, /self-serve handoff/);
assert.match(prompt, /skill, plugin, provider, channel, bundle, or MCP integration/);
assert.match(prompt, /metadata, entrypoint, permissions, secrets\/config/);
assert.match(prompt, /should not open a ClawHub issue/);
assert.match(prompt, /open a ClawHub PR/);
assert.match(prompt, /publish the package on the contributor's behalf/);
});

test("review prompt requires upgrade and preference overwrite checks", () => {
const prompt = readFileSync("prompts/review-item.md", "utf8");

Expand Down