Skip to content

Commit 413ef25

Browse files
authored
Merge pull request #262 from openclaw/codex/cluster-fixer-docs-ops
docs: document cluster fixer operations visibility
2 parents 58fd04b + c5ce402 commit 413ef25

6 files changed

Lines changed: 495 additions & 1 deletion

File tree

dashboard/worker.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,7 @@ async function statusSnapshot(env, ctx) {
10701070
automerge: automerge.items,
10711071
closed_items: closed.items,
10721072
closed_stats: closed.stats,
1073+
operation_counts: operationEventCounts(storedEvents),
10731074
events: recentActivityEvents(storedEvents, closed.items),
10741075
failed_runs: failedRuns.slice(0, 10).map((run) => workflowRunSummary(run)),
10751076
},
@@ -2351,6 +2352,70 @@ function recentActivityEvents(storedEvents, closedItems) {
23512352
.slice(0, 25);
23522353
}
23532354

2355+
function operationEventCounts(storedEvents) {
2356+
const counts = {
2357+
inherited_label_cleanups: 0,
2358+
self_heal_conflict_repairs: 0,
2359+
failed_review_retries: 0,
2360+
failed_review_retry_exhaustions: 0,
2361+
bot_owned_proof_decisions_requested: 0,
2362+
bot_owned_proof_dispatches: 0,
2363+
};
2364+
for (const event of Array.isArray(storedEvents) ? storedEvents : []) {
2365+
countOperationEvent(event, counts);
2366+
}
2367+
return counts;
2368+
}
2369+
2370+
function countOperationEvent(event, counts) {
2371+
const key = [event.event_type, event.mode, event.stage, event.status]
2372+
.map((value) =>
2373+
String(value || "")
2374+
.toLowerCase()
2375+
.replaceAll("-", "_"),
2376+
)
2377+
.join(" ");
2378+
if (
2379+
key.includes("inherited_label_cleanup") ||
2380+
key.includes("replacement_label_cleanup") ||
2381+
key.includes("removed_inherited_labels")
2382+
) {
2383+
counts.inherited_label_cleanups += 1;
2384+
}
2385+
if (
2386+
key.includes("self_heal_conflict") ||
2387+
key.includes("conflict_self_heal") ||
2388+
key.includes("clawsweeper_self_rebase")
2389+
) {
2390+
counts.self_heal_conflict_repairs += 1;
2391+
}
2392+
if (
2393+
key.includes("failed_review_retry_exhausted") ||
2394+
key.includes("failed_review_retries_exhausted")
2395+
) {
2396+
counts.failed_review_retry_exhaustions += 1;
2397+
} else if (key.includes("failed_review_retry")) {
2398+
counts.failed_review_retries += 1;
2399+
}
2400+
if (
2401+
key.includes("bot_owned_proof_decision_requested") ||
2402+
key.includes("maintainer_proof_decision_requested") ||
2403+
key.includes("needs_maintainer_proof_decision") ||
2404+
key.includes("bot_proof_decision_planned") ||
2405+
key.includes("bot_proof_decision_posted")
2406+
) {
2407+
counts.bot_owned_proof_decisions_requested += 1;
2408+
}
2409+
if (
2410+
key.includes("bot_owned_proof_dispatched") ||
2411+
key.includes("bot_owned_proof_capture_dispatched") ||
2412+
key.includes("bot_proof_mantis_request_planned") ||
2413+
key.includes("bot_proof_mantis_request_posted")
2414+
) {
2415+
counts.bot_owned_proof_dispatches += 1;
2416+
}
2417+
}
2418+
23542419
function activityEventFromClosedItem(item) {
23552420
return {
23562421
event_type: "clawsweeper.item_closed",
@@ -4100,6 +4165,8 @@ a:hover { color: #89c8ff; text-decoration: underline; }
41004165
<h2>✅ Closed by ClawSweeper</h2>
41014166
<div id="closed-stats"></div>
41024167
<div id="closed"></div>
4168+
<h2>🧭 Operations</h2>
4169+
<div id="operations"></div>
41034170
<h2>📡 Recent Activity</h2>
41044171
<div id="events"></div>
41054172
</aside>
@@ -4224,6 +4291,7 @@ function renderDashboard(data, note) {
42244291
renderAutomerge(data.recent.automerge || []);
42254292
renderClosedStats(data.recent.closed_stats);
42264293
renderClosedItems(data.recent.closed_items || []);
4294+
renderOperations(data.recent.operation_counts);
42274295
renderEvents(data.recent.events || []);
42284296
}
42294297
function renderPipeline(rows) {
@@ -4276,6 +4344,18 @@ function renderClosedStats(stats) {
42764344
const safe = stats || { total: 0, issues: 0, prs: 0, window_hours: 24 };
42774345
document.getElementById("closed-stats").innerHTML = '<div class="closed-stats"><div class="closed-stat"><span>' + esc((safe.window_hours || 24) + "h total") + '</span><strong>' + fmt.format(safe.total || 0) + '</strong></div><div class="closed-stat"><span>Issues</span><strong>' + fmt.format(safe.issues || 0) + '</strong></div><div class="closed-stat"><span>PRs</span><strong>' + fmt.format(safe.prs || 0) + '</strong></div></div>';
42784346
}
4347+
function renderOperations(counts) {
4348+
const safe = counts || {};
4349+
const rows = [
4350+
["Inherited labels", safe.inherited_label_cleanups || 0],
4351+
["Conflict self-heal", safe.self_heal_conflict_repairs || 0],
4352+
["Review retries", safe.failed_review_retries || 0],
4353+
["Retry exhausted", safe.failed_review_retry_exhaustions || 0],
4354+
["Proof decisions", safe.bot_owned_proof_decisions_requested || 0],
4355+
["Proof dispatches", safe.bot_owned_proof_dispatches || 0]
4356+
];
4357+
document.getElementById("operations").innerHTML = '<div class="closed-stats">' + rows.map(row => '<div class="closed-stat"><span>' + esc(row[0]) + '</span><strong>' + fmt.format(row[1]) + '</strong></div>').join("") + '</div>';
4358+
}
42794359
function renderEvents(rows) {
42804360
if (!rows.length) {
42814361
document.getElementById("events").innerHTML = '<div class="empty">Listening for signals from the fleet...</div>';

docs/proof-nudges.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@ A pull request is eligible only when all of these are true:
2222

2323
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.
2424

25+
## Bot-Owned Proof Handling
26+
27+
Bot-owned replacement PRs are deliberately outside the contributor nudge lane.
28+
Do not make normal proof nudges comment on bot-authored PRs as if a contributor
29+
needs to respond. The `bot-proof` lane handles ClawSweeper-owned PRs that are
30+
blocked on real behavior proof.
31+
32+
The bot-owned lane is status-only unless an approved Mantis proof suggestion is
33+
available. It is eligible only when the live PR is open and not draft, the
34+
author is the ClawSweeper GitHub App, the latest
35+
ClawSweeper review says real behavior proof blocks merge, and that review head
36+
SHA still matches the live head SHA. It must skip PRs with `proof: supplied`,
37+
`proof: sufficient`, or `proof: override`.
38+
39+
When the review includes an approved Mantis-style proof suggestion, the lane
40+
posts a durable Mantis proof request comment. Otherwise it updates one durable
41+
status comment asking maintainers to choose proof capture, proof override, or
42+
pause. It does not post contributor reminders.
43+
44+
For dashboard accounting, status-only maintainer requests use
45+
`bot_proof_decision_planned` or `bot_proof_decision_posted`. Mantis proof
46+
requests use `bot_proof_mantis_request_planned` or
47+
`bot_proof_mantis_request_posted`. Hosted dashboard events with those tokens are
48+
counted in the proof operation counters.
49+
2550
## Marker
2651

2752
Cooldown state lives in the reminder comment body:
@@ -70,11 +95,16 @@ Scheduled operation uses repository variables:
7095
- `CLAWSWEEPER_PROOF_NUDGES_LIMIT`: optional scheduled batch size, default `10`.
7196
- `CLAWSWEEPER_PROOF_NUDGES_MIN_AGE_DAYS`: optional first-nudge age gate, default `5`.
7297
- `CLAWSWEEPER_PROOF_NUDGES_COOLDOWN_DAYS`: optional same-head cooldown, default `7`.
98+
- `CLAWSWEEPER_BOT_PROOF_SCHEDULED=1`: include the bot-owned proof lane in scheduled runs.
99+
- `CLAWSWEEPER_BOT_PROOF_EXECUTE=1`: allow scheduled bot-owned proof runs to post status comments and labels. Without this, scheduled bot-proof runs remain dry-run only.
73100

74101
Suggested rollout:
75102

76103
1. Run the proof-nudge workflow manually with `execute=false`.
77104
2. Set `CLAWSWEEPER_PROOF_NUDGES_SCHEDULED=1` to collect scheduled dry-run reports.
78105
3. Set `CLAWSWEEPER_PROOF_NUDGES_EXECUTE=1` only after the scheduled reports look correct.
106+
4. Run the bot-owned proof lane manually with `bot_proof=true` and `bot_proof_execute=false`.
107+
5. Set `CLAWSWEEPER_BOT_PROOF_SCHEDULED=1` only after dry-run reports look correct.
108+
6. Set `CLAWSWEEPER_BOT_PROOF_EXECUTE=1` only after generated comments and labels have been reviewed.
79109

80110
This first version intentionally has no auto-close behavior. Any escalation after repeated proof nudges needs a separate maintainer policy decision.

docs/repair/auto-update-prs.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,66 @@ state is enough to dispatch repair. That lets Codex rebase or resolve conflicts
259259
before the next exact-head review instead of waiting for a later pass marker or
260260
new maintainer comment.
261261

262+
## ClawSweeper-Owned Conflict Self-Heal
263+
264+
The conflict self-heal lane is for open pull requests that ClawSweeper itself
265+
owns. When enabled, it may dispatch a bounded repair for a PR only when all of these
266+
are true:
267+
268+
- the author is the ClawSweeper GitHub App;
269+
- the head branch is in the base repository and starts with `clawsweeper/`;
270+
- the PR is open and the live merge state is `CONFLICTING`, `DIRTY`, or
271+
`BEHIND`;
272+
- the exact head SHA is captured before dispatch and still matches when the
273+
worker starts;
274+
- no waiting or dispatched self-heal attempt already covers that PR/head;
275+
- the self-heal per-head and per-PR caps still allow another attempt.
276+
277+
Self-heal is repair-only. It does not add `clawsweeper:automerge`, it does not
278+
request a merge, and it does not treat a successful rebase as approval. After a
279+
successful push, the next required step is an exact-item ClawSweeper review for
280+
the new head. The PR can merge only through the normal automerge gates or a
281+
human maintainer path.
282+
283+
The self-heal scanner has dedicated attempt caps:
284+
285+
```bash
286+
--max-attempts-per-pr 10
287+
--max-attempts-per-head 2
288+
```
289+
290+
The durable status comment records the detected merge state, targeted head SHA,
291+
job path, repair run URL, and current status. The generated job uses
292+
`job_intent: clawsweeper_self_rebase`, blocks close/merge/label actions, and
293+
pins `expected_head_sha` so stale jobs stop without mutating. For dashboard
294+
accounting, emit a `/api/events` payload whose event type, mode, stage, or
295+
status contains `clawsweeper_self_rebase` or `conflict_self_heal`.
296+
297+
## Replacement Label Cleanup
298+
299+
Replacement PR labels must describe the replacement PR, not stale lifecycle
300+
state from a source PR. Replacement creation filters source labels so it does
301+
not copy `close:*`, `stale`, `rating:*`, `status:*`, `proof:*`,
302+
`triage: needs-real-behavior-proof`, `merge-risk:*`, `size:*`, or `P*`
303+
priority labels.
304+
305+
Use the cleanup command to inspect existing open ClawSweeper replacement PRs
306+
for inherited source labels:
307+
308+
```bash
309+
pnpm run repair:cleanup-replacement-labels -- --repo openclaw/openclaw
310+
```
311+
312+
The command writes `.artifacts/replacement-label-cleanup.json` by default. It
313+
is dry-run by default. To remove lifecycle labels from matching PRs, pass
314+
`--execute` with `CLAWSWEEPER_ALLOW_EXECUTE=1`; execute mode removes only
315+
`stale` and `close:*` labels.
316+
317+
For dashboard accounting, publish the cleanup total through `pnpm run status`
318+
with `--inherited-label-cleanups`, or send a `/api/events` payload whose event
319+
type, mode, stage, or status contains `replacement_label_cleanup` or
320+
`inherited_label_cleanup`.
321+
262322
ClawSweeper edits one durable review comment in place. The router keys its
263323
ledger by comment id plus `updated_at`, and response markers include the target
264324
PR head SHA, so an edited ClawSweeper comment can trigger a new repair after

docs/repair/operations.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,45 @@ commands, finalizers, self-heal, gates, and ledgers, see
77
For the trusted ClawSweeper-to-ClawSweeper PR repair loop, see
88
[`docs/repair/auto-update-prs.md`](auto-update-prs.md).
99

10+
## Cluster Repair Operations Counters
11+
12+
The README dashboard and hosted live dashboard expose passive counters for the
13+
cluster repair lanes. These counters are observational only; they do not
14+
enable repair, merge, close, proof dispatch, or label mutation.
15+
16+
Record-backed dashboard counters read recent markdown records and count these
17+
signals:
18+
19+
- `replacement_label_cleanup` or `inherited_label_cleanup` for replacement-label cleanup;
20+
- `clawsweeper_self_rebase` or `conflict_self_heal` for ClawSweeper-owned conflict repair;
21+
- `failed_review_retry_status: dispatched` or an action containing
22+
`failed_review_retry` for an exact failed-review retry dispatch;
23+
- `failed_review_retry_status: exhausted` or an action containing
24+
`failed_review_retry_exhausted` for retry cap exhaustion;
25+
- `bot_proof_decision_planned`, `bot_proof_decision_posted`, or
26+
`needs_maintainer_proof_decision` for status-only maintainer proof decisions;
27+
- `bot_proof_mantis_request_planned` or `bot_proof_mantis_request_posted` for
28+
approved Mantis proof requests.
29+
30+
The hosted dashboard counts the same names from `/api/events` payloads when the
31+
event type, mode, stage, or status contains the matching token. Lanes that
32+
mutate GitHub or dispatch another workflow should emit a durable record or
33+
status comment and a hosted-dashboard event when that hook is available.
34+
35+
Manual workflow status updates can publish the same counters directly:
36+
37+
```bash
38+
pnpm run status -- \
39+
--state "Working" \
40+
--detail "Cluster repair dry-run completed." \
41+
--inherited-label-cleanups 3 \
42+
--self-heal-conflict-repairs 1 \
43+
--failed-review-retries 2 \
44+
--failed-review-retry-exhaustions 1 \
45+
--bot-owned-proof-decisions-requested 1 \
46+
--bot-owned-proof-dispatches 0
47+
```
48+
1049
For commit-review findings, ClawSweeper dispatches
1150
`clawsweeper_commit_finding` to this repository. ClawSweeper fetches the latest
1251
markdown report, writes `results/commit-findings/<repo-slug>/<sha>.md`, and

0 commit comments

Comments
 (0)