Compare commits
3 Commits
main
...
smoke/claw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e342b4407 | ||
|
|
29fd537196 | ||
|
|
0ef767cf5e |
12
.github/workflows/sweep.yml
vendored
12
.github/workflows/sweep.yml
vendored
@ -169,11 +169,15 @@ jobs:
|
||||
esac
|
||||
target_owner="${target_repo%%/*}"
|
||||
target_name="${target_repo#*/}"
|
||||
target_checkout_dir="$target_name"
|
||||
if [ "$target_repo" = "${{ github.repository }}" ]; then
|
||||
target_checkout_dir="${target_name}-target"
|
||||
fi
|
||||
{
|
||||
echo "target_repo=$target_repo"
|
||||
echo "target_repo_owner=$target_owner"
|
||||
echo "target_repo_name=$target_name"
|
||||
echo "target_checkout_dir=$target_name"
|
||||
echo "target_checkout_dir=$target_checkout_dir"
|
||||
echo "item_number=$item_number"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
@ -461,11 +465,15 @@ jobs:
|
||||
fi
|
||||
target_owner="${target_repo%%/*}"
|
||||
target_name="${target_repo#*/}"
|
||||
target_checkout_dir="$target_name"
|
||||
if [ "$target_repo" = "${{ github.repository }}" ]; then
|
||||
target_checkout_dir="${target_name}-target"
|
||||
fi
|
||||
{
|
||||
echo "target_repo=$target_repo"
|
||||
echo "target_repo_owner=$target_owner"
|
||||
echo "target_repo_name=$target_name"
|
||||
echo "target_checkout_dir=$target_name"
|
||||
echo "target_checkout_dir=$target_checkout_dir"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create target read token
|
||||
|
||||
23
README.md
23
README.md
@ -1,7 +1,8 @@
|
||||
# ClawSweeper
|
||||
|
||||
ClawSweeper is the conservative maintenance bot for OpenClaw repositories. It
|
||||
currently covers `openclaw/openclaw` and `openclaw/clawhub`.
|
||||
currently covers `openclaw/openclaw`, `openclaw/clawhub`, and self-review for
|
||||
`openclaw/clawsweeper`.
|
||||
|
||||
It has two independent lanes:
|
||||
|
||||
@ -15,8 +16,8 @@ It has two independent lanes:
|
||||
## Capabilities
|
||||
|
||||
- **Repository profiles:** per-repository rules live in
|
||||
`src/repository-profiles.ts`, so OpenClaw and ClawHub can share the same
|
||||
engine while keeping different apply limits.
|
||||
`src/repository-profiles.ts`, so OpenClaw, ClawHub, and ClawSweeper can share
|
||||
the same engine while keeping different apply limits.
|
||||
- **Issue and PR intake:** scheduled runs scan open issues and pull requests,
|
||||
while target repositories can forward exact issue/PR events with
|
||||
`repository_dispatch` for low-latency one-item reviews.
|
||||
@ -81,9 +82,10 @@ Issues with an open PR that references them using GitHub closing syntax such as
|
||||
Open issue/PR pairs from the same author stay open together unless the paired
|
||||
item is already resolved or a maintainer explicitly asks to close one side.
|
||||
|
||||
Repository profiles can further narrow apply. ClawHub is intentionally stricter:
|
||||
it reviews every issue and PR, but apply may close only PRs where current `main`
|
||||
already implements the proposed change with source-backed evidence.
|
||||
Repository profiles can further narrow apply. ClawHub and ClawSweeper self-review
|
||||
are intentionally stricter: they review issues and PRs, but apply may close only
|
||||
PRs where current `main` already implements the proposed change with
|
||||
source-backed evidence.
|
||||
|
||||
## Dashboard
|
||||
|
||||
@ -704,10 +706,11 @@ proposals later. Scheduled apply runs process both issues and pull requests by
|
||||
default, subject to the selected repository profile; pass `target_repo`,
|
||||
`apply_kind=issue`, or `apply_kind=pull_request` to narrow a manual run.
|
||||
|
||||
Scheduled runs cover both configured profiles. `openclaw/openclaw` keeps the
|
||||
existing cadence; `openclaw/clawhub` runs on offset review/apply/audit crons so
|
||||
its reports live under `records/openclaw-clawhub/` without colliding with
|
||||
default repo records.
|
||||
Scheduled runs cover the configured product profiles. `openclaw/openclaw` keeps
|
||||
the existing cadence; `openclaw/clawhub` runs on offset review/apply/audit crons
|
||||
so its reports live under `records/openclaw-clawhub/` without colliding with
|
||||
default repo records. `openclaw/clawsweeper` is available for manual and event
|
||||
self-review smoke tests.
|
||||
|
||||
Target repositories can opt into event-level latency by installing the
|
||||
dispatcher workflow in [docs/target-dispatcher.md](docs/target-dispatcher.md).
|
||||
|
||||
@ -226,7 +226,7 @@ For a maintainer-facing architecture map of the automation lanes, see
|
||||
[`docs/INTERNAL_FEATURES.md`](docs/INTERNAL_FEATURES.md).
|
||||
|
||||
For the ClawSweeper feedback loop that updates existing generated PRs, see
|
||||
[`docs/auto-update-prs.md`](docs/auto-update-prs.md).
|
||||
[`docs/repair/auto-update-prs.md`](auto-update-prs.md).
|
||||
|
||||
That loop is marker-driven. ClawSweeper comments use hidden
|
||||
`clawsweeper-verdict:*` markers, and only actionable PR feedback includes
|
||||
@ -312,7 +312,7 @@ Supported commands:
|
||||
```
|
||||
|
||||
`status` and `explain` post a short status reply. `fix ci`, `address review`,
|
||||
and `rebase` dispatch the normal `cluster-worker.yml` repair path, but only for
|
||||
and `rebase` dispatch the normal `repair-cluster-worker.yml` repair path, but only for
|
||||
existing ClawSweeper PRs identified by the `clawsweeper/*` branch.
|
||||
`automerge` opts an open PR into the bounded review/fix/merge loop. `approve`
|
||||
is maintainer-only exact-head approval after a human-review pause; it clears
|
||||
|
||||
@ -21,7 +21,7 @@ The loop is intentionally small:
|
||||
to review that PR head.
|
||||
3. The comment router sees trusted ClawSweeper feedback.
|
||||
4. ClawSweeper dispatches the existing or adopted job through
|
||||
`cluster-worker.yml`.
|
||||
`repair-cluster-worker.yml`.
|
||||
5. The repair worker pushes another commit to the source branch if it finds a
|
||||
safe, narrow fix, or opens a credited replacement when the source branch
|
||||
cannot be safely updated.
|
||||
@ -177,7 +177,7 @@ five automatic ClawSweeper-triggered repair iterations. The per-PR cap is total
|
||||
across all head SHAs and stops the automatic review/repair loop even when every
|
||||
iteration produces a new commit.
|
||||
|
||||
Runs for the same job path and mode share the `cluster-worker.yml` concurrency
|
||||
Runs for the same job path and mode share the `repair-cluster-worker.yml` concurrency
|
||||
group, so repeated dispatches queue instead of racing the same branch.
|
||||
|
||||
ClawSweeper edits one durable review comment in place. The router keys its
|
||||
@ -227,13 +227,13 @@ as:
|
||||
|
||||
Workflow:
|
||||
|
||||
- `.github/workflows/comment-router.yml`
|
||||
- `.github/workflows/repair-comment-router.yml`
|
||||
|
||||
Scripts:
|
||||
|
||||
- `scripts/comment-router.ts`
|
||||
- `scripts/comment-router-core.ts`
|
||||
- `scripts/comment-router-utils.ts`
|
||||
- `src/repair/comment-router.ts`
|
||||
- `src/repair/comment-router-core.ts`
|
||||
- `src/repair/comment-router-utils.ts`
|
||||
|
||||
Durable state:
|
||||
|
||||
@ -255,7 +255,7 @@ Syntax and workflow checks:
|
||||
|
||||
```bash
|
||||
pnpm run check
|
||||
actionlint .github/workflows/comment-router.yml
|
||||
actionlint .github/workflows/repair-comment-router.yml
|
||||
```
|
||||
|
||||
Dry-run the router against live recent comments:
|
||||
|
||||
@ -106,7 +106,7 @@ replacement PR. Direct mutation still happens outside Codex.
|
||||
|
||||
## Cloud Worker Flow
|
||||
|
||||
Workflow: `.github/workflows/cluster-worker.yml`
|
||||
Workflow: `.github/workflows/repair-cluster-worker.yml`
|
||||
|
||||
The cluster worker has two jobs:
|
||||
|
||||
@ -285,7 +285,7 @@ The finalizer scans open ClawSweeper PRs in the target repo. It finds PRs by the
|
||||
- security hold
|
||||
|
||||
When `--dispatch-repairs --execute` is enabled, it dispatches the existing
|
||||
cluster job back through `cluster-worker.yml` instead of creating another PR.
|
||||
cluster job back through `repair-cluster-worker.yml` instead of creating another PR.
|
||||
The idempotency key includes target repo, PR number, and head SHA, so the same
|
||||
PR/head is not repeatedly repaired unless `--allow-repeat` is used.
|
||||
|
||||
@ -295,8 +295,8 @@ clearly transient jobs, and pass branch-caused failures into the repair prompt.
|
||||
|
||||
## Self-Heal Failed ClawSweeper Runs
|
||||
|
||||
Workflow: `.github/workflows/self-heal.yml`
|
||||
Script: `scripts/self-heal-failed-runs.ts`
|
||||
Workflow: `.github/workflows/repair-self-heal.yml`
|
||||
Script: `src/repair/self-heal-failed-runs.ts`
|
||||
|
||||
Self-heal retries failed ClawSweeper cluster-worker runs. It reads published
|
||||
`results/runs/*.json`, selects the latest failed run per source job, skips jobs
|
||||
@ -309,11 +309,11 @@ finalizer/comment command repair path.
|
||||
|
||||
## Maintainer Comment Routing
|
||||
|
||||
Workflow: `.github/workflows/comment-router.yml`
|
||||
Workflow: `.github/workflows/repair-comment-router.yml`
|
||||
Scripts:
|
||||
|
||||
- `scripts/comment-router.ts`
|
||||
- `scripts/comment-router-core.ts`
|
||||
- `src/repair/comment-router.ts`
|
||||
- `src/repair/comment-router-core.ts`
|
||||
|
||||
Comment routing scans recent target-repo issue/PR comments and accepts only
|
||||
maintainer-authored commands. Default allowed GitHub `author_association`
|
||||
@ -326,7 +326,7 @@ values:
|
||||
Contributor comments are ignored without a reply.
|
||||
|
||||
The generated-PR auto-update design is documented in
|
||||
[`docs/auto-update-prs.md`](auto-update-prs.md). That lane lets trusted
|
||||
[`docs/repair/auto-update-prs.md`](auto-update-prs.md). That lane lets trusted
|
||||
ClawSweeper comments dispatch a repair run for an existing ClawSweeper PR or a
|
||||
PR explicitly opted into `clawsweeper:automerge` without allowing arbitrary
|
||||
comment authors to trigger work.
|
||||
@ -372,7 +372,7 @@ Behavior:
|
||||
Repair commands apply to existing ClawSweeper PRs and PRs opted into
|
||||
`clawsweeper:automerge`. The router finds ClawSweeper PRs by the
|
||||
`clawsweeper/*` branch, resolves or creates the backing job, posts one
|
||||
idempotent response marker, and dispatches `cluster-worker.yml`.
|
||||
idempotent response marker, and dispatches `repair-cluster-worker.yml`.
|
||||
|
||||
Trusted ClawSweeper comments become `clawsweeper_auto_repair`. Preferred
|
||||
comments use hidden `clawsweeper-verdict:*` markers and include
|
||||
|
||||
@ -5,7 +5,7 @@ commands, finalizers, self-heal, gates, and ledgers, see
|
||||
[`docs/INTERNAL_FEATURES.md`](INTERNAL_FEATURES.md).
|
||||
|
||||
For the trusted ClawSweeper-to-ClawSweeper PR repair loop, see
|
||||
[`docs/auto-update-prs.md`](auto-update-prs.md).
|
||||
[`docs/repair/auto-update-prs.md`](auto-update-prs.md).
|
||||
|
||||
For commit-review findings, ClawSweeper dispatches
|
||||
`clawsweeper_commit_finding` to this repository. ClawSweeper fetches the latest
|
||||
@ -194,7 +194,7 @@ Repair commands apply to existing ClawSweeper PRs and to PRs opted into
|
||||
`clawsweeper/*` branch prefix. Opted-in non-ClawSweeper PRs get an adopted job
|
||||
at `jobs/<owner>/inbox/automerge-<owner>-<repo>-<pr>.md`.
|
||||
The router posts one idempotent reply with a hidden marker and dispatches the
|
||||
normal `cluster-worker.yml` repair path. It records processed comment versions
|
||||
normal `repair-cluster-worker.yml` repair path. It records processed comment versions
|
||||
in `results/comment-router.json`. For durable ClawSweeper comments,
|
||||
idempotency is per comment id plus GitHub `updated_at`, and response markers
|
||||
include the target PR head SHA. That lets edited ClawSweeper comments wake
|
||||
|
||||
@ -2,7 +2,12 @@ import type { JsonValue, LooseRecord } from "./json-types.js";
|
||||
import { DEFAULT_ALLOWED_REPOSITORY_PERMISSIONS } from "./comment-router-core.js";
|
||||
import { currentProjectRepo, readMaxLiveWorkers } from "./lib.js";
|
||||
import { assertRepo, commaSet, positiveInteger } from "./comment-router-utils.js";
|
||||
import { DEFAULT_HEAD_PREFIX, DEFAULT_TARGET_REPO } from "./constants.js";
|
||||
import {
|
||||
DEFAULT_HEAD_PREFIX,
|
||||
DEFAULT_TARGET_REPO,
|
||||
REPAIR_CLUSTER_WORKFLOW,
|
||||
SWEEP_WORKFLOW,
|
||||
} from "./constants.js";
|
||||
export { DEFAULT_HEAD_PREFIX, DEFAULT_TARGET_REPO } from "./constants.js";
|
||||
|
||||
const DEFAULT_ALLOWED_ASSOCIATIONS = ["OWNER", "MEMBER", "COLLABORATOR"];
|
||||
@ -44,7 +49,7 @@ export function readCommentRouterConfig(args: LooseRecord): CommentRouterConfig
|
||||
);
|
||||
const workflow = stringSetting(
|
||||
args.workflow ?? process.env.CLAWSWEEPER_COMMENT_WORKFLOW,
|
||||
"cluster-worker.yml",
|
||||
REPAIR_CLUSTER_WORKFLOW,
|
||||
);
|
||||
const reviewRepo = stringSetting(
|
||||
args["review-repo"] ?? process.env.CLAWSWEEPER_REVIEW_REPO,
|
||||
@ -52,7 +57,7 @@ export function readCommentRouterConfig(args: LooseRecord): CommentRouterConfig
|
||||
);
|
||||
const reviewWorkflow = stringSetting(
|
||||
args["review-workflow"] ?? process.env.CLAWSWEEPER_REVIEW_WORKFLOW,
|
||||
"sweep.yml",
|
||||
SWEEP_WORKFLOW,
|
||||
);
|
||||
const runner = stringSetting(
|
||||
args.runner ?? process.env.CLAWSWEEPER_WORKER_RUNNER,
|
||||
|
||||
@ -2,6 +2,9 @@ export const DEFAULT_TARGET_REPO = "openclaw/openclaw";
|
||||
export const DEFAULT_HEAD_PREFIX = "clawsweeper/";
|
||||
export const DEFAULT_LABEL = "clawsweeper";
|
||||
|
||||
export const REPAIR_CLUSTER_WORKFLOW = "repair-cluster-worker.yml";
|
||||
export const SWEEP_WORKFLOW = "sweep.yml";
|
||||
|
||||
export const CLAWSWEEPER_LABEL = "clawsweeper";
|
||||
export const CLAWSWEEPER_LABEL_COLOR = "F97316";
|
||||
export const CLAWSWEEPER_LABEL_DESCRIPTION = "Tracked by ClawSweeper automation";
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
waitForLiveWorkerCapacity,
|
||||
} from "./lib.js";
|
||||
import { sleepMs } from "./timing.js";
|
||||
import { REPAIR_CLUSTER_WORKFLOW } from "./constants.js";
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const defaultRunner = process.env.CLAWSWEEPER_WORKER_RUNNER ?? "blacksmith-4vcpu-ubuntu-2404";
|
||||
@ -23,7 +24,7 @@ const defaultExecutionRunner =
|
||||
const mode = args.mode ?? "plan";
|
||||
const runner = args.runner ?? defaultRunner;
|
||||
const executionRunner = args["execution-runner"] ?? args.execution_runner ?? defaultExecutionRunner;
|
||||
const workflow = args.workflow ?? "cluster-worker.yml";
|
||||
const workflow = args.workflow ?? REPAIR_CLUSTER_WORKFLOW;
|
||||
const repo = String(args.repo ?? currentProjectRepo());
|
||||
const model = String(args.model ?? process.env.CLAWSWEEPER_MODEL ?? "gpt-5.5");
|
||||
const maxLiveWorkers = readMaxLiveWorkers(args);
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from "./lib.js";
|
||||
import { ghJson, ghText } from "./github-cli.js";
|
||||
import { sleepMs } from "./timing.js";
|
||||
import { DEFAULT_TARGET_REPO, REVIEW_BOTS } from "./constants.js";
|
||||
import { DEFAULT_TARGET_REPO, REPAIR_CLUSTER_WORKFLOW, REVIEW_BOTS } from "./constants.js";
|
||||
import { numberEnv } from "./env-utils.js";
|
||||
import { compactText, escapeRegExp } from "./text-utils.js";
|
||||
|
||||
@ -35,7 +35,7 @@ const writeReport = Boolean(args["write-report"]);
|
||||
const execute = Boolean(args.execute);
|
||||
const dispatchRepairs = Boolean(args["dispatch-repairs"] || args.dispatch || execute);
|
||||
const workflow = String(
|
||||
args.workflow ?? process.env.CLAWSWEEPER_FINALIZER_WORKFLOW ?? "cluster-worker.yml",
|
||||
args.workflow ?? process.env.CLAWSWEEPER_FINALIZER_WORKFLOW ?? REPAIR_CLUSTER_WORKFLOW,
|
||||
);
|
||||
const runner = String(
|
||||
args.runner ?? process.env.CLAWSWEEPER_WORKER_RUNNER ?? "blacksmith-4vcpu-ubuntu-2404",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ghJson } from "./github-cli.js";
|
||||
import type { JsonValue, LooseRecord } from "./json-types.js";
|
||||
import { REPAIR_CLUSTER_WORKFLOW } from "./constants.js";
|
||||
import { currentProjectRepo } from "./project-repo.js";
|
||||
import { sleepMs } from "./timing.js";
|
||||
|
||||
@ -20,7 +21,7 @@ export function readMaxLiveWorkers(args: LooseRecord = {}) {
|
||||
|
||||
export function liveWorkerCapacity({
|
||||
repo = currentProjectRepo(),
|
||||
workflow = "cluster-worker.yml",
|
||||
workflow = REPAIR_CLUSTER_WORKFLOW,
|
||||
requested = 1,
|
||||
maxLiveWorkers = DEFAULT_MAX_LIVE_WORKERS,
|
||||
}: LooseRecord = {}) {
|
||||
@ -61,7 +62,7 @@ export function waitForLiveWorkerCapacity(options: LooseRecord = {}) {
|
||||
);
|
||||
if (requestedCount > max) {
|
||||
throw new Error(
|
||||
`refusing dispatch: requested ${requestedCount} ${options.workflow ?? "cluster-worker.yml"} workers exceeds max-live-workers=${max}`,
|
||||
`refusing dispatch: requested ${requestedCount} ${options.workflow ?? REPAIR_CLUSTER_WORKFLOW} workers exceeds max-live-workers=${max}`,
|
||||
);
|
||||
}
|
||||
const pollMs = readPositiveInteger(
|
||||
@ -91,13 +92,13 @@ export function waitForLiveWorkerCapacity(options: LooseRecord = {}) {
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`timed out waiting for ${options.workflow ?? "cluster-worker.yml"} capacity: ${latest?.active ?? "unknown"} active + ${requestedCount} requested exceeds max-live-workers=${max}`,
|
||||
`timed out waiting for ${options.workflow ?? REPAIR_CLUSTER_WORKFLOW} capacity: ${latest?.active ?? "unknown"} active + ${requestedCount} requested exceeds max-live-workers=${max}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function listActiveWorkflowRuns({
|
||||
repo = currentProjectRepo(),
|
||||
workflow = "cluster-worker.yml",
|
||||
workflow = REPAIR_CLUSTER_WORKFLOW,
|
||||
}: LooseRecord = {}) {
|
||||
const runs: LooseRecord[] = [];
|
||||
for (const status of ACTIVE_WORKFLOW_STATUSES) {
|
||||
|
||||
@ -16,9 +16,10 @@ import {
|
||||
} from "./lib.js";
|
||||
import { ghJson, ghText } from "./github-cli.js";
|
||||
import { sleepMs } from "./timing.js";
|
||||
import { REPAIR_CLUSTER_WORKFLOW } from "./constants.js";
|
||||
|
||||
const DEFAULT_REPO = currentProjectRepo();
|
||||
const DEFAULT_WORKFLOW = "cluster-worker.yml";
|
||||
const DEFAULT_WORKFLOW = REPAIR_CLUSTER_WORKFLOW;
|
||||
const DEFAULT_RUNNER = process.env.CLAWSWEEPER_WORKER_RUNNER ?? "blacksmith-4vcpu-ubuntu-2404";
|
||||
const DEFAULT_EXECUTION_RUNNER =
|
||||
process.env.CLAWSWEEPER_EXECUTION_RUNNER ?? "blacksmith-16vcpu-ubuntu-2404";
|
||||
|
||||
@ -15,9 +15,10 @@ import {
|
||||
} from "./lib.js";
|
||||
import { ghJson, ghText } from "./github-cli.js";
|
||||
import { sleepMs } from "./timing.js";
|
||||
import { REPAIR_CLUSTER_WORKFLOW } from "./constants.js";
|
||||
|
||||
const DEFAULT_REPO = currentProjectRepo();
|
||||
const DEFAULT_WORKFLOW = "cluster-worker.yml";
|
||||
const DEFAULT_WORKFLOW = REPAIR_CLUSTER_WORKFLOW;
|
||||
const DEFAULT_RUNNER = process.env.CLAWSWEEPER_WORKER_RUNNER ?? "blacksmith-4vcpu-ubuntu-2404";
|
||||
const DEFAULT_EXECUTION_RUNNER =
|
||||
process.env.CLAWSWEEPER_EXECUTION_RUNNER ?? "blacksmith-16vcpu-ubuntu-2404";
|
||||
|
||||
@ -4,6 +4,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { hasSecuritySignalText, parseArgs, parseJob, repoRoot, validateJob } from "./lib.js";
|
||||
import { ghJson } from "./github-cli.js";
|
||||
import { REPAIR_CLUSTER_WORKFLOW } from "./constants.js";
|
||||
import { readJsonFileIfExists as readJson } from "./json-file.js";
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
@ -291,7 +292,7 @@ function readActiveClusterRuns() {
|
||||
"--repo",
|
||||
repo,
|
||||
"--workflow",
|
||||
"cluster-worker.yml",
|
||||
REPAIR_CLUSTER_WORKFLOW,
|
||||
"--status",
|
||||
status,
|
||||
"--limit",
|
||||
|
||||
@ -60,6 +60,18 @@ export const REPOSITORY_PROFILES: readonly RepositoryProfile[] = [
|
||||
pull_request: ["implemented_on_main"],
|
||||
},
|
||||
},
|
||||
{
|
||||
targetRepo: "openclaw/clawsweeper",
|
||||
slug: "openclaw-clawsweeper",
|
||||
displayName: "ClawSweeper",
|
||||
checkoutDir: "clawsweeper",
|
||||
promptNote:
|
||||
"Use the ClawSweeper source tree and current main branch. Review bot automation, workflow, and documentation changes conservatively. Only propose auto-close for pull requests that are certainly implemented on main; keep issues open for maintainer triage.",
|
||||
applyCloseRules: {
|
||||
issue: [],
|
||||
pull_request: ["implemented_on_main"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function repositoryProfileFor(targetRepo: string): RepositoryProfile {
|
||||
|
||||
@ -353,6 +353,29 @@ test("ClawHub policy only allows implemented-on-main PR close proposals", () =>
|
||||
assert.equal(nonImplementedPr.actionTaken, "skipped_invalid_decision");
|
||||
});
|
||||
|
||||
test("ClawSweeper policy allows self PR review without issue auto-close", () => {
|
||||
const implementedPr = validateCloseDecision(
|
||||
item({
|
||||
repo: "openclaw/clawsweeper",
|
||||
kind: "pull_request",
|
||||
url: "https://github.com/openclaw/clawsweeper/pull/17",
|
||||
}),
|
||||
closeDecision(),
|
||||
);
|
||||
assert.equal(implementedPr.ok, true);
|
||||
|
||||
const implementedIssue = validateCloseDecision(
|
||||
item({
|
||||
repo: "openclaw/clawsweeper",
|
||||
kind: "issue",
|
||||
url: "https://github.com/openclaw/clawsweeper/issues/17",
|
||||
}),
|
||||
closeDecision(),
|
||||
);
|
||||
assert.equal(implementedIssue.ok, false);
|
||||
assert.equal(implementedIssue.actionTaken, "skipped_invalid_decision");
|
||||
});
|
||||
|
||||
test("review policy changes force fresh complete reports back into planning", () => {
|
||||
const reviewedAt = new Date().toISOString();
|
||||
const review = {
|
||||
|
||||
@ -289,7 +289,7 @@ test("renderResponse reports trusted repair dispatches without losing guardrails
|
||||
target: { head_sha: "def456" },
|
||||
},
|
||||
{
|
||||
workflow: "cluster-worker.yml",
|
||||
workflow: "repair-cluster-worker.yml",
|
||||
job_path: "jobs/openclaw/inbox/example.md",
|
||||
mode: "autonomous",
|
||||
model: "gpt-5.5",
|
||||
@ -298,7 +298,7 @@ test("renderResponse reports trusted repair dispatches without losing guardrails
|
||||
|
||||
assert.match(body, /Thanks, ClawSweeper/);
|
||||
assert.match(body, /clawsweeper-command:456:2026-04-29T07:12:31Z:clawsweeper_auto_repair:def456/);
|
||||
assert.match(body, /cluster-worker\.yml/);
|
||||
assert.match(body, /repair-cluster-worker\.yml/);
|
||||
assert.match(body, /safe credited replacement/);
|
||||
assert.match(body, /narrow fix/);
|
||||
assert.doesNotMatch(body, /ClawSweeper Repair/i);
|
||||
@ -359,7 +359,7 @@ test("renderResponse reports automerge repair dispatches", () => {
|
||||
target: { head_sha: "def457" },
|
||||
},
|
||||
{
|
||||
workflow: "cluster-worker.yml",
|
||||
workflow: "repair-cluster-worker.yml",
|
||||
job_path: "jobs/openclaw/inbox/automerge-openclaw-openclaw-74156.md",
|
||||
mode: "autonomous",
|
||||
model: "gpt-5.5",
|
||||
@ -367,7 +367,7 @@ test("renderResponse reports automerge repair dispatches", () => {
|
||||
);
|
||||
|
||||
assert.match(body, /picked up the repair feedback/);
|
||||
assert.match(body, /cluster-worker\.yml/);
|
||||
assert.match(body, /repair-cluster-worker\.yml/);
|
||||
assert.match(body, /automerge-openclaw-openclaw-74156/);
|
||||
assert.doesNotMatch(body, /did not dispatch/);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user