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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ checkpoint, and status-only commits are intentionally omitted.

### Changed

- Made ClawSweeper review reports and `proof: sufficient` or `proof: override` the proof-nudge authority, retiring `proof: supplied` and PR-context hygiene labels from proof state. Thanks @hannesrudolph.

### Fixed

- Kept issue implementation intake and dispatch off the Codex worker runner by default so saturated repair capacity cannot stall eligible issue backfills before worker admission.
Expand Down
20 changes: 3 additions & 17 deletions dashboard/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ const GITHUB_APP_TOKEN_DEFAULT_TTL_MS = 50 * 60_000;
const PR_PROOF_LABEL_NAMES = [
"triage: needs-real-behavior-proof",
"triage: mock-only-proof",
"proof: supplied",
"proof: sufficient",
"proof: override",
"mantis: telegram-visible-proof",
Expand Down Expand Up @@ -207,16 +206,9 @@ const PR_PROOF_VIEWS = [
},
{
id: "missing-proof",
title: "No proof supplied",
description: "Proof is requested, but no supplied, sufficient, or override label is present.",
title: "Needs proof review",
description: "Proof is requested, but ClawSweeper has not marked it sufficient or overridden.",
allLabels: ["triage: needs-real-behavior-proof"],
withoutLabels: ["proof: supplied", "proof: sufficient", "proof: override"],
},
{
id: "supplied-awaiting-review",
title: "Supplied, needs review",
description: "Proof has been supplied, but ClawSweeper has not marked it sufficient.",
allLabels: ["proof: supplied"],
withoutLabels: ["proof: sufficient", "proof: override"],
},
{
Expand Down Expand Up @@ -2547,7 +2539,6 @@ function proofStateFromLabels(labels) {
return "Sufficient + needs label";
}
if (has("proof: sufficient")) return "Sufficient";
if (has("proof: supplied")) return "Supplied, needs review";
if (has("triage: mock-only-proof")) return "Mock-only proof";
if (has("triage: needs-real-behavior-proof")) return "Needs proof";
if (has("mantis: telegram-visible-proof")) return "Telegram proof";
Expand Down Expand Up @@ -4546,12 +4537,7 @@ function prProofTriagePageConfig() {
metrics: [
{ label: "Proof triage PRs", view: "proof-triage", detail: "proof-related labels" },
{ label: "Needs proof", view: "needs-proof", detail: "real behavior proof requested" },
{ label: "No proof supplied", view: "missing-proof", detail: "most stuck bucket" },
{
label: "Supplied, needs review",
view: "supplied-awaiting-review",
detail: "waiting on sufficiency decision",
},
{ label: "Needs proof review", view: "missing-proof", detail: "most stuck bucket" },
{
label: "Proof sufficient",
view: "sufficient-proof",
Expand Down
10 changes: 4 additions & 6 deletions docs/pr-proof-triage-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ The worker reads a fixed set of proof-related labels from the target repository:

- `triage: needs-real-behavior-proof`
- `triage: mock-only-proof`
- `proof: supplied`
- `proof: sufficient`
- `proof: override`
- `mantis: telegram-visible-proof`
Expand All @@ -30,16 +29,15 @@ The focused views are derived from high-signal label combinations:
| ------------------------ | -------------------------------------------------------------------------------------------------------- |
| Proof triage | any configured proof-related label |
| Needs proof | `triage: needs-real-behavior-proof` |
| No proof supplied | `triage: needs-real-behavior-proof` without `proof: supplied`, `proof: sufficient`, or `proof: override` |
| Supplied, needs review | `proof: supplied` without `proof: sufficient` or `proof: override` |
| Needs proof review | `triage: needs-real-behavior-proof` without `proof: sufficient` or `proof: override` |
| Proof sufficient | `proof: sufficient` |
| Mock-only proof | `triage: mock-only-proof` |
| Telegram proof | `mantis: telegram-visible-proof` |
| Sufficient + needs label | `triage: needs-real-behavior-proof` and `proof: sufficient` |

The API queries each proof view directly so the main stuck buckets are not
undercounted by a broad first-page snapshot. The `No proof supplied` and
`Supplied, needs review` views load up to `PR_PROOF_ITEMS_PER_VIEW`; the default
undercounted by a broad first-page snapshot. The `Needs proof review` view loads
up to `PR_PROOF_ITEMS_PER_VIEW`; the default
is 500, with an upper bound of 1,000 because GitHub Search only exposes the
first 1,000 results for a query. Broader summary views load fewer rows while
still using GitHub's `total_count`, so the metric cards stay accurate without
Expand Down Expand Up @@ -80,6 +78,6 @@ Keep this dashboard read-only:
- no repair dispatch

A later phase could add contributor reminders for PRs that remain in the
`No proof supplied` view for a maintainer-defined period, or route selected PRs
`Needs proof review` view for a maintainer-defined period, or route selected PRs
to maintainers who can validate behavior in Crabbox. That should be a separate
opt-in workflow with clear wording, cooldowns, and maintainer control.
9 changes: 4 additions & 5 deletions docs/proof-nudges.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Read when changing the ClawSweeper lane that reminds pull request authors to add

## Scope

The proof-nudge lane is read-mostly triage hygiene. It can post a polite reminder comment on open pull requests that are stuck on `triage: needs-real-behavior-proof`, but it does not close pull requests, merge pull requests, change labels, request reviews, or modify review records.
The proof-nudge lane is read-mostly triage hygiene. It can post a polite reminder comment when the latest ClawSweeper review still requires real behavior proof, but it does not close pull requests, merge pull requests, change labels, request reviews, or modify review records.

The lane uses the latest ClawSweeper review report plus the live pull request state. It does not scrape the visible review comment for policy.

Expand All @@ -13,14 +13,13 @@ The lane uses the latest ClawSweeper review report plus the live pull request st
A pull request is eligible only when all of these are true:

- The live item is an open pull request.
- The live pull request still has `triage: needs-real-behavior-proof`.
- The latest report still says real behavior proof blocks merge.
- The report head SHA matches the live pull request head SHA.
- The pull request is past the first-nudge age gate, defaulting to 5 days.
- The author has not commented recently and the head commit is not recent.
- There is no same-head proof nudge inside the cooldown window, defaulting to 7 days.

The lane skips maintainer-authored, bot-authored, security-sensitive, and release-style pull requests. It also skips pull requests with `proof: supplied`, `proof: sufficient`, or `proof: override`, because those need review or policy handling rather than another contributor reminder.
The lane skips maintainer-authored, bot-authored, security-sensitive, and release-style pull requests. It also skips pull requests with `proof: sufficient` or `proof: override`, because those need review or policy handling rather than another contributor reminder. OpenClaw's `triage: needs-pr-context` label is separate contributor-body hygiene and does not decide real-proof sufficiency.

## Bot-Owned Proof Handling

Expand All @@ -33,8 +32,8 @@ The bot-owned lane is status-only unless an approved Mantis proof suggestion is
available. It is eligible only when the live PR is open and not draft, the
author is the ClawSweeper GitHub App, the latest
ClawSweeper review says real behavior proof blocks merge, and that review head
SHA still matches the live head SHA. It must skip PRs with `proof: supplied`,
`proof: sufficient`, or `proof: override`.
SHA still matches the live head SHA. It must skip PRs with `proof: sufficient`
or `proof: override`.

When the review includes an approved Mantis-style proof suggestion, the lane
posts a durable Mantis proof request comment. Otherwise it updates one durable
Expand Down
2 changes: 1 addition & 1 deletion docs/repair/auto-update-prs.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ status contains `clawsweeper_self_rebase` or `conflict_self_heal`.
Replacement PR labels must describe the replacement PR, not stale lifecycle
state from a source PR. Replacement creation filters source labels so it does
not copy `close:*`, `stale`, `rating:*`, `status:*`, `proof:*`,
`triage: needs-real-behavior-proof`, `merge-risk:*`, `size:*`, or `P*`
`triage: needs-real-behavior-proof`, `triage: needs-pr-context`, `merge-risk:*`, `size:*`, or `P*`
priority labels.

Use the cleanup command to inspect existing open ClawSweeper replacement PRs
Expand Down
20 changes: 4 additions & 16 deletions src/clawsweeper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,6 @@ type ProofNudgeAction =
| "proof_nudge_planned"
| "skipped_not_pull_request"
| "skipped_not_open"
| "skipped_missing_label"
| "skipped_policy_exempt"
| "skipped_stale_report_head"
| "skipped_recent_author_activity"
Expand Down Expand Up @@ -1099,8 +1098,6 @@ const UNCONFIRMED_PRODUCT_DIRECTION_EXEMPT_LABELS = new Set([
]);
const PROOF_OVERRIDE_LABEL = "proof: override";
const PROOF_SUFFICIENT_LABEL = "proof: sufficient";
const PROOF_SUPPLIED_LABEL = "proof: supplied";
const REAL_BEHAVIOR_PROOF_REQUIRED_LABEL = "triage: needs-real-behavior-proof";
const PROOF_NUDGE_MARKER_PREFIX = "<!-- clawsweeper-proof-nudge";
const PROOF_NUDGE_MARKER_VERSION = "1";
const BOT_PROOF_DECISION_MARKER_PREFIX = "<!-- clawsweeper-bot-proof-decision";
Expand Down Expand Up @@ -11662,13 +11659,6 @@ function proofNudgeEligibility(options: ProofNudgeEligibilityOptions): ProofNudg
reason: `type is ${options.item.kind}`,
};
}
if (!hasNormalizedLabel(options.item.labels, REAL_BEHAVIOR_PROOF_REQUIRED_LABEL)) {
return {
eligible: false,
action: "skipped_missing_label",
reason: `${REAL_BEHAVIOR_PROOF_REQUIRED_LABEL} is not present`,
};
}
const lockedReason = lockedConversationApplyReason(options.item);
if (lockedReason) {
return {
Expand All @@ -11679,13 +11669,12 @@ function proofNudgeEligibility(options: ProofNudgeEligibilityOptions): ProofNudg
}
if (
hasNormalizedLabel(options.item.labels, PROOF_OVERRIDE_LABEL) ||
hasNormalizedLabel(options.item.labels, PROOF_SUFFICIENT_LABEL) ||
hasNormalizedLabel(options.item.labels, PROOF_SUPPLIED_LABEL)
hasNormalizedLabel(options.item.labels, PROOF_SUFFICIENT_LABEL)
) {
return {
eligible: false,
action: "skipped_policy_exempt",
reason: "proof is already supplied, sufficient, or overridden",
reason: "proof is already sufficient or overridden",
};
}
if (isMaintainerAuthored(options.item)) {
Expand Down Expand Up @@ -11872,13 +11861,12 @@ function botProofEligibility(options: BotProofEligibilityOptions): BotProofEligi
}
if (
hasNormalizedLabel(options.item.labels, PROOF_OVERRIDE_LABEL) ||
hasNormalizedLabel(options.item.labels, PROOF_SUFFICIENT_LABEL) ||
hasNormalizedLabel(options.item.labels, PROOF_SUPPLIED_LABEL)
hasNormalizedLabel(options.item.labels, PROOF_SUFFICIENT_LABEL)
) {
return {
eligible: false,
action: "skipped_policy_exempt",
reason: "proof is already supplied, sufficient, or overridden",
reason: "proof is already sufficient or overridden",
};
}
if (!realBehaviorProofBlocksBotOwnedMerge(options.markdown)) {
Expand Down
4 changes: 3 additions & 1 deletion src/repair/replacement-labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export function replacementSourceLabelCopyable(value: string): boolean {
) {
return false;
}
if (key === "triage: needs-real-behavior-proof") return false;
if (key === "triage: needs-real-behavior-proof" || key === "triage: needs-pr-context") {
return false;
}
if (/^p[0-3]$/.test(key)) return false;
return true;
}
Expand Down
6 changes: 3 additions & 3 deletions test/proof-nudge-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ test("bot proof handling skips overrides, stale heads, drafts, and non-ClawSweep
}).action,
"skipped_draft",
);
for (const label of ["proof: override", "proof: supplied", "proof: sufficient"] as const) {
for (const label of ["proof: override", "proof: sufficient"] as const) {
assert.equal(
botProofEligibilityForTest({
item: proofNudgeItem({
Expand Down Expand Up @@ -553,13 +553,13 @@ test("proof nudges skip stale report heads and proof-policy exemptions", () => {
);
assert.equal(
proofNudgeEligibilityForTest({
item: proofNudgeItem({ labels: ["triage: needs-real-behavior-proof", "proof: supplied"] }),
item: proofNudgeItem({ labels: ["proof: supplied"] }),
markdown: proofNudgeReport(),
headSha: "abc123def456",
headCommittedAt: "2026-01-01T00:00:00Z",
now: Date.parse("2026-01-10T00:00:00Z"),
}).action,
"skipped_policy_exempt",
"proof_nudge_planned",
);
});

Expand Down
2 changes: 2 additions & 0 deletions test/repair/replacement-labels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ test("replacement PRs do not copy source review, proof, status, risk, size, or p
"status: 📣 needs proof",
"proof: missing",
"triage: needs-real-behavior-proof",
"triage: needs-pr-context",
"merge-risk: 🚨 compatibility",
"size: M",
"P1",
Expand All @@ -85,6 +86,7 @@ test("replacement source label filter documents denied classes", () => {
"status: 📣 needs proof",
"proof: missing",
"triage: needs-real-behavior-proof",
"triage: needs-pr-context",
"merge-risk: 🚨 compatibility",
"size: M",
"P3",
Expand Down
11 changes: 4 additions & 7 deletions test/review-prompt-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,16 +433,13 @@ Reason: Maintainers should review the proof before merge.
});

test("ClawSweeper proof judgement controls the sufficient proof label", () => {
assert.deepEqual(realBehaviorProofSufficientLabelsForTest(["proof: supplied"], "sufficient"), [
"proof: supplied",
assert.deepEqual(realBehaviorProofSufficientLabelsForTest(["bug"], "sufficient"), [
"bug",
"proof: sufficient",
]);
assert.deepEqual(
realBehaviorProofSufficientLabelsForTest(
["proof: supplied", "proof: sufficient"],
"insufficient",
),
["proof: supplied"],
realBehaviorProofSufficientLabelsForTest(["bug", "proof: sufficient"], "insufficient"),
["bug"],
);
assert.deepEqual(realBehaviorProofSufficientLabelsForTest(["proof: sufficient"], "missing"), []);
});
Expand Down