feat: add clawsweeper dashboard renderer
This commit is contained in:
parent
179a0376bc
commit
6bffc1b29e
58
.github/workflows/dashboard.yml
vendored
Normal file
58
.github/workflows/dashboard.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
name: dashboard
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
repository_dispatch:
|
||||||
|
types: [clawsweeper_dashboard_refresh]
|
||||||
|
schedule:
|
||||||
|
- cron: "*/15 * * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: clawsweeper-dashboard
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
render:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
path: dashboard
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: openclaw/clawsweeper
|
||||||
|
path: clawsweeper
|
||||||
|
filter: blob:none
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
|
||||||
|
- name: Enable pnpm
|
||||||
|
run: corepack enable
|
||||||
|
|
||||||
|
- name: Render dashboard
|
||||||
|
working-directory: dashboard
|
||||||
|
run: |
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
pnpm run check
|
||||||
|
pnpm run render -- --source ../clawsweeper
|
||||||
|
|
||||||
|
- name: Commit dashboard
|
||||||
|
working-directory: dashboard
|
||||||
|
run: |
|
||||||
|
if [ -z "$(git status --porcelain -- README.md)" ]; then
|
||||||
|
echo "Dashboard unchanged."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add README.md
|
||||||
|
git commit -m "chore: update dashboard"
|
||||||
|
git push
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
.DS_Store
|
||||||
249
README.md
Normal file
249
README.md
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
# ClawSweeper Dashboard
|
||||||
|
|
||||||
|
Generated dashboard for [openclaw/clawsweeper](https://github.com/openclaw/clawsweeper).
|
||||||
|
|
||||||
|
## Sweep Dashboard
|
||||||
|
|
||||||
|
Last source update: May 1, 2026, 06:11 UTC
|
||||||
|
|
||||||
|
### Fleet
|
||||||
|
|
||||||
|
| Metric | Count |
|
||||||
|
| --- | ---: |
|
||||||
|
| Covered repositories | 3 |
|
||||||
|
| Open review records | 7651 |
|
||||||
|
| Archived closed records | 15505 |
|
||||||
|
| Fresh reviews, 7d | 7606 |
|
||||||
|
| Proposed closes awaiting apply | 6 |
|
||||||
|
| Work candidates awaiting promotion | 2413 |
|
||||||
|
| Failed or stale reviews | 21 |
|
||||||
|
|
||||||
|
### Current Runs
|
||||||
|
|
||||||
|
| Repository | State | Updated | Run |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | Idle | unknown | _none_ |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | Idle | unknown | _none_ |
|
||||||
|
| [openclaw/clawsweeper](https://github.com/openclaw/clawsweeper) | Idle | unknown | _none_ |
|
||||||
|
|
||||||
|
### Repositories
|
||||||
|
|
||||||
|
| Repository | Open records | Archived | Fresh | Proposed closes | Work candidates | Failed/stale | Last review | Last close |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | 6722 | 15479 | 6677 | 6 | 2367 | 9 | May 1, 2026, 06:11 UTC | May 1, 2026, 06:05 UTC |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | 924 | 26 | 924 | 0 | 45 | 12 | Apr 29, 2026, 22:22 UTC | Apr 29, 2026, 17:17 UTC |
|
||||||
|
| [openclaw/clawsweeper](https://github.com/openclaw/clawsweeper) | 5 | 0 | 5 | 0 | 1 | 0 | May 1, 2026, 02:03 UTC | unknown |
|
||||||
|
|
||||||
|
### Work Candidates
|
||||||
|
|
||||||
|
| Repository | Item | Title | Priority | Reviewed | Report |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75183](https://github.com/openclaw/openclaw/pull/75183) | fix: simplify bundled runtime dependency repair | high | May 1, 2026, 06:08 UTC | [records/openclaw-openclaw/items/75183.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75183.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#72025](https://github.com/openclaw/openclaw/pull/72025) | fix(signal): enable inbound status reactions | high | May 1, 2026, 06:08 UTC | [records/openclaw-openclaw/items/72025.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/72025.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#72677](https://github.com/openclaw/openclaw/pull/72677) | fix(cron): warn on main heartbeat handoff ghost runs | high | May 1, 2026, 06:05 UTC | [records/openclaw-openclaw/items/72677.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/72677.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#74377](https://github.com/openclaw/openclaw/issues/74377) | [Bug]: tools array empty at Anthropic provider despite 17 tools computed in attempt.ts (Telegram channel) | high | May 1, 2026, 06:04 UTC | [records/openclaw-openclaw/items/74377.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/74377.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#73243](https://github.com/openclaw/openclaw/pull/73243) | fix(diagnostics): abort stuck sessions | high | May 1, 2026, 06:04 UTC | [records/openclaw-openclaw/items/73243.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/73243.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75155](https://github.com/openclaw/openclaw/issues/75155) | Talk Mode: user voice transcripts don't render in WebChat/Control UI thread | high | May 1, 2026, 06:04 UTC | [records/openclaw-openclaw/items/75155.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75155.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75206](https://github.com/openclaw/openclaw/issues/75206) | Auto-update leaves stale chunk reference: conversation-runtime imports missing inbound.runtime-*.js → Teleg... | high | May 1, 2026, 06:04 UTC | [records/openclaw-openclaw/items/75206.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75206.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75153](https://github.com/openclaw/openclaw/issues/75153) | [Feature]: expose channels.start / stop / restart via CLI so a wedged channel can recover without container... | high | May 1, 2026, 06:04 UTC | [records/openclaw-openclaw/items/75153.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75153.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75456](https://github.com/openclaw/openclaw/issues/75456) | security audit --json can hang while staging plugin runtime deps and scanning backup extension dirs | high | May 1, 2026, 06:03 UTC | [records/openclaw-openclaw/items/75456.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75456.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75171](https://github.com/openclaw/openclaw/issues/75171) | [Bug]: TypeError: (0, _pluginSdk.createReplyPrefixContext) is not a function after update | high | May 1, 2026, 06:03 UTC | [records/openclaw-openclaw/items/75171.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75171.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75131](https://github.com/openclaw/openclaw/issues/75131) | [Bug]: delivery-queue: send-retry creates fresh UUIDs (not idempotent); recovery should fail-permanent on T... | high | May 1, 2026, 06:03 UTC | [records/openclaw-openclaw/items/75131.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75131.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75151](https://github.com/openclaw/openclaw/issues/75151) | [Bug]: Context overflow reset can map sessionFile to nonexistent transcript, orphaning real session history | high | May 1, 2026, 06:03 UTC | [records/openclaw-openclaw/items/75151.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75151.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75180](https://github.com/openclaw/openclaw/pull/75180) | fix(reply): delivery-aware media tracking in block reply pipeline (#75156) | high | May 1, 2026, 06:00 UTC | [records/openclaw-openclaw/items/75180.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75180.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75452](https://github.com/openclaw/openclaw/issues/75452) | Heartbeat per-turn model override persists after turn completes (2026.4.29) | high | May 1, 2026, 05:57 UTC | [records/openclaw-openclaw/items/75452.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75452.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75425](https://github.com/openclaw/openclaw/pull/75425) | fix(media): fall back to qrcode/lib subpath when bare specifier fails to resolve | high | May 1, 2026, 05:57 UTC | [records/openclaw-openclaw/items/75425.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75425.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#71324](https://github.com/openclaw/openclaw/pull/71324) | fix(webchat): hide legacy runtime transcript wrappers | high | May 1, 2026, 05:56 UTC | [records/openclaw-openclaw/items/71324.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/71324.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#58683](https://github.com/openclaw/openclaw/pull/58683) | feat(heartbeat): add time-of-day schedule for variable intervals | high | May 1, 2026, 05:56 UTC | [records/openclaw-openclaw/items/58683.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/58683.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#58650](https://github.com/openclaw/openclaw/pull/58650) | fix(reply-runtime): preserve final answer in /reasoning on with block replies | high | May 1, 2026, 05:56 UTC | [records/openclaw-openclaw/items/58650.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/58650.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#58608](https://github.com/openclaw/openclaw/issues/58608) | Session corruption from orphaned tool_result blocks survives restart (overload/timeout interruption) | high | May 1, 2026, 05:55 UTC | [records/openclaw-openclaw/items/58608.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/58608.md) |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75447](https://github.com/openclaw/openclaw/issues/75447) | [Bug]: openclaw update installls old whatsapp extension - update should not install extensions that are old... | high | May 1, 2026, 05:49 UTC | [records/openclaw-openclaw/items/75447.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-openclaw/items/75447.md) |
|
||||||
|
|
||||||
|
### Recently Closed
|
||||||
|
|
||||||
|
| Repository | Item | Title | Reason | Closed | Report |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1434](https://github.com/openclaw/clawhub/issues/1434) | Sonarbay-skill incorrectly flaged. | closed externally after review | Apr 29, 2026, 17:17 UTC | [records/openclaw-clawhub/closed/1434.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1434.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1376](https://github.com/openclaw/clawhub/issues/1376) | False positive: openclaw-workspace-sync flagged as suspicious | closed externally after review | Apr 29, 2026, 17:06 UTC | [records/openclaw-clawhub/closed/1376.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1376.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1812](https://github.com/openclaw/clawhub/issues/1812) | Skill be flagged as suspicious and being marked hidden | closed externally after review | Apr 29, 2026, 13:48 UTC | [records/openclaw-clawhub/closed/1812.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1812.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1861](https://github.com/openclaw/clawhub/pull/1861) | feat: add owner rescan security surfaces | closed externally after review | Apr 28, 2026, 23:32 UTC | [records/openclaw-clawhub/closed/1861.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1861.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1870](https://github.com/openclaw/clawhub/pull/1870) | fix: hide admin CLI help from non-admins | none | Apr 28, 2026, 23:24 UTC | [records/openclaw-clawhub/closed/1870.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1870.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1869](https://github.com/openclaw/clawhub/pull/1869) | [codex] Use GitHub App auth for publish gate lookups | closed externally after review | Apr 28, 2026, 22:18 UTC | [records/openclaw-clawhub/closed/1869.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1869.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1841](https://github.com/openclaw/clawhub/pull/1841) | fix: calibrate VT Code Insight moderation | closed externally after review | Apr 28, 2026, 08:18 UTC | [records/openclaw-clawhub/closed/1841.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1841.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1830](https://github.com/openclaw/clawhub/issues/1830) | False positive: skill-factory incorrectly flagged as suspicious | closed externally after review | Apr 28, 2026, 08:18 UTC | [records/openclaw-clawhub/closed/1830.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1830.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1517](https://github.com/openclaw/clawhub/issues/1517) | [Appeal] Skill Wrongly Flagged: abu-shotai/ai-video-remix | closed externally after review | Apr 28, 2026, 07:43 UTC | [records/openclaw-clawhub/closed/1517.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1517.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#125](https://github.com/openclaw/clawhub/issues/125) | New Provider Plugin: ClawRouter — 30+ models, smart routing, x402 payments | closed externally after review | Apr 28, 2026, 06:41 UTC | [records/openclaw-clawhub/closed/125.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/125.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1699](https://github.com/openclaw/clawhub/issues/1699) | Plugin search returns 500, and plugin catalog breaks after page 2 | closed externally after review | Apr 28, 2026, 05:46 UTC | [records/openclaw-clawhub/closed/1699.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1699.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1736](https://github.com/openclaw/clawhub/pull/1736) | Align hover stats with denormalized counters and fix seed digest stat drift | implemented_on_main | Apr 28, 2026, 05:18 UTC | [records/openclaw-clawhub/closed/1736.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1736.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1324](https://github.com/openclaw/clawhub/pull/1324) | feat: add --dry-run flag to package publish command | implemented_on_main | Apr 28, 2026, 05:18 UTC | [records/openclaw-clawhub/closed/1324.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1324.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1240](https://github.com/openclaw/clawhub/pull/1240) | fix: use esbuild minification for safari builds | implemented_on_main | Apr 28, 2026, 05:18 UTC | [records/openclaw-clawhub/closed/1240.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1240.md) |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | [#1842](https://github.com/openclaw/clawhub/pull/1842) | fix: constrain plugin catalog queries | closed externally after review | Apr 28, 2026, 05:05 UTC | [records/openclaw-clawhub/closed/1842.md](https://github.com/openclaw/clawsweeper/blob/main/records/openclaw-clawhub/closed/1842.md) |
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Recently Reviewed</summary>
|
||||||
|
|
||||||
|
| Repository | Item | Title | Outcome | Status | Reviewed |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75448](https://github.com/openclaw/openclaw/pull/75448) | feat(ui): replace textarea with CodeMirror 6 JSON editor in raw config view | keep_open / kept_open | complete | May 1, 2026, 06:11 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75183](https://github.com/openclaw/openclaw/pull/75183) | fix: simplify bundled runtime dependency repair | keep_open / kept_open | complete | May 1, 2026, 06:08 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#72025](https://github.com/openclaw/openclaw/pull/72025) | fix(signal): enable inbound status reactions | keep_open / kept_open | complete | May 1, 2026, 06:08 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75459](https://github.com/openclaw/openclaw/pull/75459) | fix: voice-call CLI gateway delegation path actionable regressions | keep_open / kept_open | complete | May 1, 2026, 06:08 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75454](https://github.com/openclaw/openclaw/issues/75454) | 1M context window not applied for github-copilot/claude-opus-4.x-1m models (capped at 272k) | keep_open / kept_open | complete | May 1, 2026, 06:06 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75423](https://github.com/openclaw/openclaw/pull/75423) | fix(gateway): refresh stale channel health cache | keep_open / kept_open | complete | May 1, 2026, 06:06 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75327](https://github.com/openclaw/openclaw/pull/75327) | fix: deliver group/channel replies automatically when bot is @mentioned | keep_open / kept_open | complete | May 1, 2026, 06:06 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75419](https://github.com/openclaw/openclaw/issues/75419) | [Bug]: [2026.4.29] openclaw cannot accept for any input | keep_open / kept_open | complete | May 1, 2026, 06:05 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#72677](https://github.com/openclaw/openclaw/pull/72677) | fix(cron): warn on main heartbeat handoff ghost runs | keep_open / kept_open | complete | May 1, 2026, 06:05 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#74377](https://github.com/openclaw/openclaw/issues/74377) | [Bug]: tools array empty at Anthropic provider despite 17 tools computed in attempt.ts (Telegram channel) | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75406](https://github.com/openclaw/openclaw/issues/75406) | v2026.4.29 - synchronous WASM tokenizer init blocks event loop 20-79s on first LLM call (system-prompt / ru... | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#73320](https://github.com/openclaw/openclaw/pull/73320) | Limit internal task completion payloads | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#58482](https://github.com/openclaw/openclaw/pull/58482) | fix(memory-host): add AbortSignal support to batch polling (OpenAI, Voyage, Gemini) | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75279](https://github.com/openclaw/openclaw/issues/75279) | [Performance]: 3-7 minute agent response latency on Windows — 66 plugins loaded, 49 disabled, 48 skills mis... | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | [#75298](https://github.com/openclaw/openclaw/issues/75298) | Webhook plugin re-registers repeatedly whenever event loop saturates (dreaming, cron catchup, Control UI po... | keep_open / kept_open | complete | May 1, 2026, 06:04 UTC |
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Audit Health
|
||||||
|
|
||||||
|
| Repository | Status | Last audit | Missing eligible | Stale records | Protected proposed | Scan complete |
|
||||||
|
| --- | --- | --- | ---: | ---: | ---: | --- |
|
||||||
|
| [openclaw/openclaw](https://github.com/openclaw/openclaw) | _unknown_ | unknown | 0 | 0 | 0 | unknown |
|
||||||
|
| [openclaw/clawhub](https://github.com/openclaw/clawhub) | _unknown_ | unknown | 0 | 0 | 0 | unknown |
|
||||||
|
| [openclaw/clawsweeper](https://github.com/openclaw/clawsweeper) | _unknown_ | unknown | 0 | 0 | 0 | unknown |
|
||||||
|
|
||||||
|
|
||||||
|
## Repair Dashboard
|
||||||
|
|
||||||
|
Last source update: May 1, 2026, 06:03 UTC
|
||||||
|
|
||||||
|
State: Failed clusters need inspection
|
||||||
|
|
||||||
|
| Metric | Count | Rate |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Latest clusters reviewed | 362 | 100% |
|
||||||
|
| Run attempts archived | 631 | audit |
|
||||||
|
| Latest successful clusters | 332 | 91.7% |
|
||||||
|
| Latest failed clusters | 7 | 1.9% |
|
||||||
|
| Latest cancelled clusters | 2 | 0.6% |
|
||||||
|
| Needs-human clusters | 42 | 11.6% |
|
||||||
|
| Fix actions failed | 26 | 11.8% |
|
||||||
|
| Fix actions blocked | 56 | 25.5% |
|
||||||
|
| Completed close actions | 30 | 5.2% |
|
||||||
|
| Completed merge actions | 23 | 4.0% |
|
||||||
|
| Blocked mutation attempts | 269 | 46.2% |
|
||||||
|
| Skipped mutation attempts | 260 | 44.7% |
|
||||||
|
|
||||||
|
### Clusters Needing Inspection
|
||||||
|
|
||||||
|
| Cluster | State | Reason | Report | Run |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-464e57360262](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-464e57360262.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-464e57360262](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-464e57360262.md) | [25204309027](https://github.com/openclaw/clawsweeper/actions/runs/25204309027) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-e8f9c3e6dedc](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-e8f9c3e6dedc.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-e8f9c3e6dedc](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-e8f9c3e6dedc.md) | [25204170897](https://github.com/openclaw/clawsweeper/actions/runs/25204170897) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-214b3d333676](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-214b3d333676.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-214b3d333676](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-214b3d333676.md) | [25203815918](https://github.com/openclaw/clawsweeper/actions/runs/25203815918) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-76930da7ebc7](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-76930da7ebc7.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-76930da7ebc7](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-76930da7ebc7.md) | [25203495459](https://github.com/openclaw/clawsweeper/actions/runs/25203495459) |
|
||||||
|
| [automerge-openclaw-openclaw-75382](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75382.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-75382](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75382.md) | [25202542275](https://github.com/openclaw/clawsweeper/actions/runs/25202542275) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97.md) | execute_fix blocked | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97.md) | [25202885744](https://github.com/openclaw/clawsweeper/actions/runs/25202885744) |
|
||||||
|
| [automerge-openclaw-openclaw-75363](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75363.md) | fix failed | Codex /review did not pass after 2 attempt(s): No security-sensitive issue found in the diff. The artifact’s unrelated `.agents` files are absent,... | [automerge-openclaw-openclaw-75363](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75363.md) | [25202543309](https://github.com/openclaw/clawsweeper/actions/runs/25202543309) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-8989ceee50ab](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-8989ceee50ab.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-8989ceee50ab](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-8989ceee50ab.md) | [25202541355](https://github.com/openclaw/clawsweeper/actions/runs/25202541355) |
|
||||||
|
| [automerge-openclaw-openclaw-74506](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74506.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-74506](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74506.md) | [25202544672](https://github.com/openclaw/clawsweeper/actions/runs/25202544672) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-3e67ee63b4e0](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-3e67ee63b4e0.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-3e67ee63b4e0](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-3e67ee63b4e0.md) | [25200994696](https://github.com/openclaw/clawsweeper/actions/runs/25200994696) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-45b86450795d](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-45b86450795d.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-45b86450795d](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-45b86450795d.md) | [25200785766](https://github.com/openclaw/clawsweeper/actions/runs/25200785766) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-60bdb96f2c4c](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-60bdb96f2c4c.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-60bdb96f2c4c](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-60bdb96f2c4c.md) | [25200786346](https://github.com/openclaw/clawsweeper/actions/runs/25200786346) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-354084b1b320](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-354084b1b320.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-354084b1b320](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-354084b1b320.md) | [25200785151](https://github.com/openclaw/clawsweeper/actions/runs/25200785151) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-e0fe02fb0970](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-e0fe02fb0970.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-e0fe02fb0970](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-e0fe02fb0970.md) | [25200786950](https://github.com/openclaw/clawsweeper/actions/runs/25200786950) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-df0ee092f017](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-df0ee092f017.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-df0ee092f017](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-df0ee092f017.md) | [25200373309](https://github.com/openclaw/clawsweeper/actions/runs/25200373309) |
|
||||||
|
| [automerge-openclaw-openclaw-75326](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75326.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-75326](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75326.md) | [25198747424](https://github.com/openclaw/clawsweeper/actions/runs/25198747424) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-b277ae3f4c40](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-b277ae3f4c40.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-b277ae3f4c40](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-b277ae3f4c40.md) | [25198053576](https://github.com/openclaw/clawsweeper/actions/runs/25198053576) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-a102f4dede6a](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-a102f4dede6a.md) | execute_fix blocked | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests [check:changed] src/agents/agent-command.ts: core production... | [clawsweeper-commit-openclaw-openclaw-a102f4dede6a](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-a102f4dede6a.md) | [25197023690](https://github.com/openclaw/clawsweeper/actions/runs/25197023690) |
|
||||||
|
| [automerge-openclaw-openclaw-75302](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75302.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-75302](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75302.md) | [25196881228](https://github.com/openclaw/clawsweeper/actions/runs/25196881228) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-52bf20b07d6e](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-52bf20b07d6e.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-52bf20b07d6e](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-52bf20b07d6e.md) | [25195746048](https://github.com/openclaw/clawsweeper/actions/runs/25195746048) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-ef799fd57a77](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-ef799fd57a77.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-ef799fd57a77](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-ef799fd57a77.md) | [25195271001](https://github.com/openclaw/clawsweeper/actions/runs/25195271001) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-af5a1fbddb14](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-af5a1fbddb14.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-af5a1fbddb14](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-af5a1fbddb14.md) | [25192772436](https://github.com/openclaw/clawsweeper/actions/runs/25192772436) |
|
||||||
|
| [automerge-openclaw-openclaw-74716](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74716.md) | fix failed | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, extensions, extensionTests, apps, docs [check:changed] exten... | [automerge-openclaw-openclaw-74716](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74716.md) | [25192389369](https://github.com/openclaw/clawsweeper/actions/runs/25192389369) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-1d74ecd71f0f](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-1d74ecd71f0f.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-1d74ecd71f0f](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-1d74ecd71f0f.md) | [25191267120](https://github.com/openclaw/clawsweeper/actions/runs/25191267120) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-027ea5f08bd9](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-027ea5f08bd9.md) | execute_fix blocked | Codex /review did not pass after 2 attempt(s): Merge is blocked by one incomplete repair. The branch is narrow and `pnpm check:changed` plus `git d... | [clawsweeper-commit-openclaw-openclaw-027ea5f08bd9](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-027ea5f08bd9.md) | [25187047448](https://github.com/openclaw/clawsweeper/actions/runs/25187047448) |
|
||||||
|
| [automerge-openclaw-openclaw-75209](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75209.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-75209](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75209.md) | [25186718052](https://github.com/openclaw/clawsweeper/actions/runs/25186718052) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-581fbea1d653](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-581fbea1d653.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-581fbea1d653](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-581fbea1d653.md) | [25183870132](https://github.com/openclaw/clawsweeper/actions/runs/25183870132) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-54e6e3d7daf5](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-54e6e3d7daf5.md) | merge_canonical blocked | job does not allow merge | [clawsweeper-commit-openclaw-openclaw-54e6e3d7daf5](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-54e6e3d7daf5.md) | [25183184702](https://github.com/openclaw/clawsweeper/actions/runs/25183184702) |
|
||||||
|
| [automerge-openclaw-openclaw-74472](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74472.md) | merge_canonical blocked | job does not allow merge | [automerge-openclaw-openclaw-74472](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74472.md) | [25182390249](https://github.com/openclaw/clawsweeper/actions/runs/25182390249) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-82ca6ecdde80](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-82ca6ecdde80.md) | execute_fix blocked | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [clawsweeper-commit-openclaw-openclaw-82ca6ecdde80](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-82ca6ecdde80.md) | [25176627885](https://github.com/openclaw/clawsweeper/actions/runs/25176627885) |
|
||||||
|
|
||||||
|
### Fix Failure Queue
|
||||||
|
|
||||||
|
| Cluster | Status | Target | Branch/PR | Reason | Run |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-5d1ba08e3c97.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25202885744](https://github.com/openclaw/clawsweeper/actions/runs/25202885744) |
|
||||||
|
| [automerge-openclaw-openclaw-75363](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75363.md) | failed | | | Codex /review did not pass after 2 attempt(s): No security-sensitive issue found in the diff. The artifact’s unrelated `.agents` files are absent,... | [25202543309](https://github.com/openclaw/clawsweeper/actions/runs/25202543309) |
|
||||||
|
| [automerge-openclaw-openclaw-75363](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-75363.md) | blocked | | | Codex /review did not pass after 2 attempt(s): No security-sensitive issue found in the diff. The artifact’s unrelated `.agents` files are absent,... | [25202543309](https://github.com/openclaw/clawsweeper/actions/runs/25202543309) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-a102f4dede6a](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-a102f4dede6a.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests [check:changed] src/agents/agent-command.ts: core production... | [25197023690](https://github.com/openclaw/clawsweeper/actions/runs/25197023690) |
|
||||||
|
| [automerge-openclaw-openclaw-74716](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74716.md) | failed | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, extensions, extensionTests, apps, docs [check:changed] exten... | [25192389369](https://github.com/openclaw/clawsweeper/actions/runs/25192389369) |
|
||||||
|
| [automerge-openclaw-openclaw-74716](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74716.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, extensions, extensionTests, apps, docs [check:changed] exten... | [25192389369](https://github.com/openclaw/clawsweeper/actions/runs/25192389369) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-027ea5f08bd9](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-027ea5f08bd9.md) | blocked | | | Codex /review did not pass after 2 attempt(s): Merge is blocked by one incomplete repair. The branch is narrow and `pnpm check:changed` plus `git d... | [25187047448](https://github.com/openclaw/clawsweeper/actions/runs/25187047448) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-82ca6ecdde80](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-82ca6ecdde80.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25176627885](https://github.com/openclaw/clawsweeper/actions/runs/25176627885) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-ac599c9e539f](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-ac599c9e539f.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests [check:changed] src/plugins/bundled-runtime-deps-selection.ts... | [25171874076](https://github.com/openclaw/clawsweeper/actions/runs/25171874076) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-3c9437ae547a](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-3c9437ae547a.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25149929229](https://github.com/openclaw/clawsweeper/actions/runs/25149929229) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-0142c791232e](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-0142c791232e.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25147836943](https://github.com/openclaw/clawsweeper/actions/runs/25147836943) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-d7396d4ffa2f](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-d7396d4ffa2f.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25147023250](https://github.com/openclaw/clawsweeper/actions/runs/25147023250) |
|
||||||
|
| [automerge-openclaw-openclaw-74638](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74638.md) | failed | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, docs [check:changed] src/config/config.schema-regressions.te... | [25146762143](https://github.com/openclaw/clawsweeper/actions/runs/25146762143) |
|
||||||
|
| [automerge-openclaw-openclaw-74638](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74638.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, docs [check:changed] src/config/config.schema-regressions.te... | [25146762143](https://github.com/openclaw/clawsweeper/actions/runs/25146762143) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-25d2e9bdace3](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-25d2e9bdace3.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=apps [check:changed] apps/macos/Sources/OpenClaw/GatewayLaunchAgentManager.sw... | [25145494152](https://github.com/openclaw/clawsweeper/actions/runs/25145494152) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-58153d38af57](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-58153d38af57.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, extensions, extensionTests [check:changed] extensions/codex/... | [25143847817](https://github.com/openclaw/clawsweeper/actions/runs/25143847817) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-0b59964ec945](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-0b59964ec945.md) | blocked | | | Codex /review failed: structured output was not written to replacement-codex-review-1.json; stdout={"type":"thread.started","thread_id":"019ddbc8-0... | [25140762037](https://github.com/openclaw/clawsweeper/actions/runs/25140762037) |
|
||||||
|
| [automerge-openclaw-openclaw-74528](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74528.md) | blocked | | | Codex /review failed: structured output was not written to replacement-codex-review-1.json; stdout={"type":"thread.started","thread_id":"019ddba8-1... | [25139586999](https://github.com/openclaw/clawsweeper/actions/runs/25139586999) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-db6951088a19](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-db6951088a19.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, extensions, extensionTests [check:changed] extensions/telegr... | [25128072554](https://github.com/openclaw/clawsweeper/actions/runs/25128072554) |
|
||||||
|
| [clawsweeper-commit-openclaw-openclaw-6a4c866b6a8b](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-6a4c866b6a8b.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_REPAIR_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25094690632](https://github.com/openclaw/clawsweeper/actions/runs/25094690632) |
|
||||||
|
| [ghcrawl-166004-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-166004-agentic-merge.md) | blocked | | | Codex /review did not pass after 2 attempt(s): Cannot perform the review without inspecting the repository diff and validation state. | [25087637821](https://github.com/openclaw/clawsweeper/actions/runs/25087637821) |
|
||||||
|
| [ghcrawl-156717-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156717-autonomous-smoke.md) | blocked | | | validation command failed (pnpm check:changed): [check:changed] lanes=core, coreTests, docs [check:changed] ui/src/styles/chat/layout.css: core pro... | [25085937628](https://github.com/openclaw/clawsweeper/actions/runs/25085937628) |
|
||||||
|
| [ghcrawl-156593-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156593-autonomous-smoke.md) | failed | | | To https://github.com/LiaoyuanNing/openclaw.git ! [remote rejected] HEAD -> fix/feishu-p2p-thread-reply (refusing to allow a GitHub App to create o... | [25084314903](https://github.com/openclaw/clawsweeper/actions/runs/25084314903) |
|
||||||
|
| [ghcrawl-156593-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156593-autonomous-smoke.md) | blocked | | | Codex /review did not pass after 2 attempt(s): Blocked. The diff is narrow and has no visible secret, dependency, workflow, install, or security-se... | [25084314903](https://github.com/openclaw/clawsweeper/actions/runs/25084314903) |
|
||||||
|
| [ghcrawl-156627-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156627-autonomous-smoke.md) | blocked | | | fix artifact is too broad for autonomous execution; split into narrower jobs or explicitly set CLAWSWEEPER_REPAIR_ALLOW_BROAD_FIX_ARTIFACTS=1 | [25070489790](https://github.com/openclaw/clawsweeper/actions/runs/25070489790) |
|
||||||
|
|
||||||
|
### Top Blocked Reasons
|
||||||
|
|
||||||
|
| Reason | Latest count | Example cluster |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
| job does not allow merge | 90 | [clawsweeper-commit-openclaw-openclaw-464e57360262](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/clawsweeper-commit-openclaw-openclaw-464e57360262.md) |
|
||||||
|
| action status is blocked | 89 | [ghcrawl-156636-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156636-autonomous-smoke.md) |
|
||||||
|
| close requires ClawSweeper Repair fix PR opened/pushed or merge executed first | 13 | [ghcrawl-156593-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156593-autonomous-smoke.md) |
|
||||||
|
| merge requires CLAWSWEEPER_REPAIR_ALLOW_MERGE=1; labeled for human review | 10 | [ghcrawl-156679-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156679-autonomous-smoke.md) |
|
||||||
|
| merge state status is UNSTABLE | 10 | [ghcrawl-156647-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156647-autonomous-smoke.md) |
|
||||||
|
| target changed since worker review | 9 | [ghcrawl-156624-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156624-autonomous-smoke.md) |
|
||||||
|
| mergeable state is CONFLICTING | 5 | [ghcrawl-156651-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156651-autonomous-smoke.md) |
|
||||||
|
| target is not listed in job candidates | 4 | [ghcrawl-156682-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156682-autonomous-smoke.md) |
|
||||||
|
| Fix-first policy blocks superseded closeout until #49430 is repaired and merged. | 2 | [ghcrawl-156585-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156585-autonomous-smoke.md) |
|
||||||
|
| Clearly superseded by the canonical PR path, but closure is blocked until the canonical fix lands. | 2 | [ghcrawl-156879-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156879-autonomous-smoke.md) |
|
||||||
|
| require_fix_before_close blocks superseded PR closeout until the canonical fix path is landed or opened as a concrete fix PR. | 2 | [ghcrawl-156789-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156789-autonomous-smoke.md) |
|
||||||
|
| canonical is not listed in job refs | 2 | [ghcrawl-156658-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156658-autonomous-smoke.md) |
|
||||||
|
| maintainer issue comment blocks low-signal auto-close | 2 | [low-signal-pr-sweep-20260425T2346-01](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/low-signal-pr-sweep-20260425t2346-01.md) |
|
||||||
|
| Blocked on the canonical fix path landing; job policy does not allow unmerged fix closeout. | 1 | [automerge-openclaw-openclaw-74666](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74666.md) |
|
||||||
|
| Superseded closeout is clear, but closure is blocked by this job's policy and requires human approval. | 1 | [automerge-openclaw-openclaw-74134](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/automerge-openclaw-openclaw-74134.md) |
|
||||||
|
|
||||||
|
### Latest Repair Closures
|
||||||
|
|
||||||
|
| Target | Action | Title | Closed | Cluster | Report | Run |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| [#59439](#59439) | close_superseded | | Apr 28, 2026, 05:25 UTC | [ghcrawl-207050-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-207050-agentic-merge.md) | [ghcrawl-207050-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-207050-agentic-merge.md) | [25035228706](https://github.com/openclaw/clawsweeper/actions/runs/25035228706) |
|
||||||
|
| [#48388](#48388) | close_fixed_by_candidate | | Apr 28, 2026, 05:23 UTC | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [25035231796](https://github.com/openclaw/clawsweeper/actions/runs/25035231796) |
|
||||||
|
| [#50435](#50435) | close_superseded | | Apr 28, 2026, 05:23 UTC | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [25035231796](https://github.com/openclaw/clawsweeper/actions/runs/25035231796) |
|
||||||
|
| [#59409](#59409) | close_fixed_by_candidate | | Apr 28, 2026, 05:23 UTC | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [25035231796](https://github.com/openclaw/clawsweeper/actions/runs/25035231796) |
|
||||||
|
| [#59431](#59431) | close_superseded | | Apr 28, 2026, 05:23 UTC | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [ghcrawl-199239-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-199239-agentic-merge.md) | [25035231796](https://github.com/openclaw/clawsweeper/actions/runs/25035231796) |
|
||||||
|
| [#61016](#61016) | close_superseded | | Apr 28, 2026, 05:00 UTC | [ghcrawl-156640-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156640-autonomous-smoke.md) | [ghcrawl-156640-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156640-autonomous-smoke.md) | [25034019917](https://github.com/openclaw/clawsweeper/actions/runs/25034019917) |
|
||||||
|
| [#49957](#49957) | close_duplicate | | Apr 28, 2026, 04:59 UTC | [ghcrawl-156664-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156664-autonomous-smoke.md) | [ghcrawl-156664-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156664-autonomous-smoke.md) | [25033552424](https://github.com/openclaw/clawsweeper/actions/runs/25033552424) |
|
||||||
|
| [#49961](#49961) | close_superseded | | Apr 28, 2026, 04:59 UTC | [ghcrawl-156664-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156664-autonomous-smoke.md) | [ghcrawl-156664-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-156664-autonomous-smoke.md) | [25033552424](https://github.com/openclaw/clawsweeper/actions/runs/25033552424) |
|
||||||
|
| [#54429](#54429) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-166002-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-166002-agentic-merge.md) | [ghcrawl-166002-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-166002-agentic-merge.md) | [24946559138](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946559138) |
|
||||||
|
| [#41992](#41992) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [24946558493](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946558493) |
|
||||||
|
| [#43242](#43242) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [24946558493](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946558493) |
|
||||||
|
| [#56298](#56298) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [24946558493](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946558493) |
|
||||||
|
| [#63094](#63094) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [24946558493](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946558493) |
|
||||||
|
| [#67622](#67622) | close_duplicate | | Apr 26, 2026, 03:04 UTC | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [ghcrawl-165992-agentic-merge](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-165992-agentic-merge.md) | [24946558493](https://github.com/openclaw/clawsweeper-repair/actions/runs/24946558493) |
|
||||||
|
| [#67029](#67029) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [24939011554](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939011554) |
|
||||||
|
| [#69886](#69886) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [24939011554](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939011554) |
|
||||||
|
| [#70353](#70353) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [24939011554](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939011554) |
|
||||||
|
| [#70395](#70395) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [24939011554](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939011554) |
|
||||||
|
| [#71133](#71133) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [ghcrawl-143819-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143819-autonomous-smoke.md) | [24939011554](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939011554) |
|
||||||
|
| [#70180](#70180) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [24939009401](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939009401) |
|
||||||
|
| [#65832](#65832) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [24939009401](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939009401) |
|
||||||
|
| [#66758](#66758) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [24939009401](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939009401) |
|
||||||
|
| [#67406](#67406) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [ghcrawl-143816-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143816-autonomous-smoke.md) | [24939009401](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939009401) |
|
||||||
|
| [#50541](#50541) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143815-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143815-autonomous-smoke.md) | [ghcrawl-143815-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143815-autonomous-smoke.md) | [24939008778](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939008778) |
|
||||||
|
| [#50691](#50691) | close_duplicate | | Apr 26, 2026, 01:36 UTC | [ghcrawl-143815-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143815-autonomous-smoke.md) | [ghcrawl-143815-autonomous-smoke](https://github.com/openclaw/clawsweeper/blob/main/results/openclaw/ghcrawl-143815-autonomous-smoke.md) | [24939008778](https://github.com/openclaw/clawsweeper-repair/actions/runs/24939008778) |
|
||||||
|
|
||||||
15
package.json
Normal file
15
package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@openclaw/clawsweeper-dashboard",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"render": "node scripts/render.mjs",
|
||||||
|
"check": "node --check scripts/*.mjs && node --test test/*.test.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.33.2"
|
||||||
|
}
|
||||||
9
pnpm-lock.yaml
generated
Normal file
9
pnpm-lock.yaml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.: {}
|
||||||
44
scripts/markdown.mjs
Normal file
44
scripts/markdown.mjs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
export function tableCell(value) {
|
||||||
|
return String(value ?? "").replaceAll("|", "\\|").replace(/\s+/g, " ").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function link(label, url) {
|
||||||
|
return `[${tableCell(label)}](${url})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function githubPath(path) {
|
||||||
|
return String(path)
|
||||||
|
.split("/")
|
||||||
|
.map((segment) => encodeURIComponent(segment))
|
||||||
|
.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTimestamp(value) {
|
||||||
|
if (!value) return "unknown";
|
||||||
|
const date = new Date(value);
|
||||||
|
if (Number.isNaN(date.getTime())) return String(value);
|
||||||
|
return new Intl.DateTimeFormat("en-US", {
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
timeZone: "UTC",
|
||||||
|
timeZoneName: "short",
|
||||||
|
}).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function percent(value, total) {
|
||||||
|
return total > 0 ? `${((value / total) * 100).toFixed(1)}%` : "0.0%";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function truncate(value, max = 110) {
|
||||||
|
const text = tableCell(value);
|
||||||
|
if (text.length <= max) return text;
|
||||||
|
return `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rowsOrNone(rows, columns) {
|
||||||
|
return rows.length ? rows.join("\n") : `| _None_ |${" |".repeat(columns - 1)}`;
|
||||||
|
}
|
||||||
40
scripts/render.mjs
Normal file
40
scripts/render.mjs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import path from "node:path";
|
||||||
|
import { renderRepairDashboard } from "./repair-dashboard.mjs";
|
||||||
|
import { writeText } from "./source.mjs";
|
||||||
|
import { renderSweepDashboard } from "./sweep-dashboard.mjs";
|
||||||
|
|
||||||
|
const args = parseArgs(process.argv.slice(2));
|
||||||
|
const sourceRoot = path.resolve(args.source ?? process.env.CLAWSWEEPER_SOURCE ?? "../clawsweeper");
|
||||||
|
const output = path.resolve(args.output ?? "README.md");
|
||||||
|
|
||||||
|
const body = `# ClawSweeper Dashboard
|
||||||
|
|
||||||
|
Generated dashboard for [` +
|
||||||
|
`openclaw/clawsweeper](https://github.com/openclaw/clawsweeper).
|
||||||
|
|
||||||
|
${renderSweepDashboard(sourceRoot)}
|
||||||
|
|
||||||
|
${renderRepairDashboard(sourceRoot)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
writeText(output, body);
|
||||||
|
console.log(JSON.stringify({ output, source: sourceRoot }));
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const parsed = {};
|
||||||
|
for (let index = 0; index < argv.length; index += 1) {
|
||||||
|
const arg = argv[index];
|
||||||
|
if (arg === "--") continue;
|
||||||
|
if (arg === "--source") parsed.source = requiredValue(argv, ++index, arg);
|
||||||
|
else if (arg === "--output") parsed.output = requiredValue(argv, ++index, arg);
|
||||||
|
else throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requiredValue(argv, index, flag) {
|
||||||
|
const value = argv[index];
|
||||||
|
if (!value || value.startsWith("--")) throw new Error(`${flag} requires a value`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
216
scripts/repair-dashboard.mjs
Normal file
216
scripts/repair-dashboard.mjs
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { formatTimestamp, link, percent, rowsOrNone, tableCell, truncate } from "./markdown.mjs";
|
||||||
|
import { jsonFiles, newestTimestamp, readJson } from "./source.mjs";
|
||||||
|
|
||||||
|
const REPORT_REPO = "https://github.com/openclaw/clawsweeper";
|
||||||
|
const CLOSE_ACTIONS = new Set([
|
||||||
|
"close",
|
||||||
|
"close_duplicate",
|
||||||
|
"close_superseded",
|
||||||
|
"close_fixed_by_candidate",
|
||||||
|
"close_low_signal",
|
||||||
|
"post_merge_close",
|
||||||
|
]);
|
||||||
|
const MERGE_ACTIONS = new Set(["merge_candidate", "merge_canonical"]);
|
||||||
|
|
||||||
|
export function renderRepairDashboard(root) {
|
||||||
|
const records = jsonFiles(path.join(root, "results", "runs"))
|
||||||
|
.map((file) => readJson(file))
|
||||||
|
.filter(Boolean);
|
||||||
|
const archivedRaw = readJson(path.join(root, "results", "archived-clusters.json"), []);
|
||||||
|
const archived = new Set(
|
||||||
|
Array.isArray(archivedRaw)
|
||||||
|
? archivedRaw
|
||||||
|
: (archivedRaw.archived_clusters ?? []).map((record) => record.cluster_id),
|
||||||
|
);
|
||||||
|
const latest = latestByCluster(records).filter((record) => !archived.has(record.cluster_id));
|
||||||
|
const allApplyRows = records.flatMap((record) =>
|
||||||
|
(record.apply_actions ?? []).map((action) => ({ record, action })),
|
||||||
|
);
|
||||||
|
const latestApplyRows = latest.flatMap((record) =>
|
||||||
|
(record.apply_actions ?? []).map((action) => ({ record, action })),
|
||||||
|
);
|
||||||
|
const latestFixRows = latest.flatMap((record) =>
|
||||||
|
(record.fix_actions ?? []).map((action) => ({ record, action })),
|
||||||
|
);
|
||||||
|
const mutationRows = allApplyRows.filter((row) =>
|
||||||
|
["executed", "blocked", "skipped"].includes(String(row.action.status ?? "")),
|
||||||
|
);
|
||||||
|
const closedRows = allApplyRows
|
||||||
|
.filter((row) => row.action.status === "executed" && CLOSE_ACTIONS.has(String(row.action.action)))
|
||||||
|
.sort(newestActionFirst);
|
||||||
|
const mergedRows = allApplyRows.filter(
|
||||||
|
(row) => row.action.status === "executed" && MERGE_ACTIONS.has(String(row.action.action)),
|
||||||
|
);
|
||||||
|
const blockedRows = latestApplyRows.filter((row) =>
|
||||||
|
["blocked", "skipped"].includes(String(row.action.status ?? "")),
|
||||||
|
);
|
||||||
|
const failedFixRows = latestFixRows.filter((row) =>
|
||||||
|
["blocked", "failed"].includes(String(row.action.status ?? "")),
|
||||||
|
);
|
||||||
|
const inspectionRows = inspectionQueue(latest, failedFixRows, blockedRows);
|
||||||
|
const totals = {
|
||||||
|
latest: latest.length,
|
||||||
|
runs: records.length,
|
||||||
|
success: latest.filter((record) => record.workflow_conclusion === "success").length,
|
||||||
|
failure: latest.filter((record) => record.workflow_conclusion === "failure").length,
|
||||||
|
cancelled: latest.filter((record) => record.workflow_conclusion === "cancelled").length,
|
||||||
|
needsHuman: latest.filter((record) => (record.needs_human ?? []).length > 0).length,
|
||||||
|
closed: closedRows.length,
|
||||||
|
merged: mergedRows.length,
|
||||||
|
blocked: allApplyRows.filter((row) => row.action.status === "blocked").length,
|
||||||
|
skipped: allApplyRows.filter((row) => row.action.status === "skipped").length,
|
||||||
|
fixFailed: latestFixRows.filter((row) => row.action.status === "failed").length,
|
||||||
|
fixBlocked: latestFixRows.filter((row) => row.action.status === "blocked").length,
|
||||||
|
mutationAttempts: mutationRows.length,
|
||||||
|
};
|
||||||
|
const lastSourceUpdate = newestTimestamp(...records.map((record) => record.published_at));
|
||||||
|
|
||||||
|
return `## Repair Dashboard
|
||||||
|
|
||||||
|
Last source update: ${formatTimestamp(lastSourceUpdate)}
|
||||||
|
|
||||||
|
State: ${repairState(totals, inspectionRows)}
|
||||||
|
|
||||||
|
| Metric | Count | Rate |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Latest clusters reviewed | ${totals.latest} | 100% |
|
||||||
|
| Run attempts archived | ${totals.runs} | audit |
|
||||||
|
| Latest successful clusters | ${totals.success} | ${percent(totals.success, totals.latest)} |
|
||||||
|
| Latest failed clusters | ${totals.failure} | ${percent(totals.failure, totals.latest)} |
|
||||||
|
| Latest cancelled clusters | ${totals.cancelled} | ${percent(totals.cancelled, totals.latest)} |
|
||||||
|
| Needs-human clusters | ${totals.needsHuman} | ${percent(totals.needsHuman, totals.latest)} |
|
||||||
|
| Fix actions failed | ${totals.fixFailed} | ${percent(totals.fixFailed, latestFixRows.length)} |
|
||||||
|
| Fix actions blocked | ${totals.fixBlocked} | ${percent(totals.fixBlocked, latestFixRows.length)} |
|
||||||
|
| Completed close actions | ${totals.closed} | ${percent(totals.closed, totals.mutationAttempts)} |
|
||||||
|
| Completed merge actions | ${totals.merged} | ${percent(totals.merged, totals.mutationAttempts)} |
|
||||||
|
| Blocked mutation attempts | ${totals.blocked} | ${percent(totals.blocked, totals.mutationAttempts)} |
|
||||||
|
| Skipped mutation attempts | ${totals.skipped} | ${percent(totals.skipped, totals.mutationAttempts)} |
|
||||||
|
|
||||||
|
### Clusters Needing Inspection
|
||||||
|
|
||||||
|
| Cluster | State | Reason | Report | Run |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(inspectionRows.slice(0, 30).map(inspectionRow), 5)}
|
||||||
|
|
||||||
|
### Fix Failure Queue
|
||||||
|
|
||||||
|
| Cluster | Status | Target | Branch/PR | Reason | Run |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(failedFixRows.slice(0, 25).map(fixRow), 6)}
|
||||||
|
|
||||||
|
### Top Blocked Reasons
|
||||||
|
|
||||||
|
| Reason | Latest count | Example cluster |
|
||||||
|
| --- | ---: | --- |
|
||||||
|
${rowsOrNone(blockedReasonRows(blockedRows), 3)}
|
||||||
|
|
||||||
|
### Latest Repair Closures
|
||||||
|
|
||||||
|
| Target | Action | Title | Closed | Cluster | Report | Run |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(closedRows.slice(0, 25).map(closeRow), 7)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function latestByCluster(records) {
|
||||||
|
const byCluster = new Map();
|
||||||
|
for (const record of records) {
|
||||||
|
const key = String(record.cluster_id ?? "");
|
||||||
|
const previous = byCluster.get(key);
|
||||||
|
if (!previous || Date.parse(record.published_at ?? "") > Date.parse(previous.published_at ?? "")) {
|
||||||
|
byCluster.set(key, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...byCluster.values()].sort(
|
||||||
|
(a, b) => Date.parse(b.published_at ?? "") - Date.parse(a.published_at ?? ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function repairState(totals, inspectionRows) {
|
||||||
|
if (totals.failure > 0) return "Failed clusters need inspection";
|
||||||
|
if (totals.fixFailed + totals.fixBlocked > 0) return "Fix execution needs repair";
|
||||||
|
if (inspectionRows.length > 0) return "Inspection needed";
|
||||||
|
return "Clean";
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectionQueue(latest, failedFixRows, blockedRows) {
|
||||||
|
const rows = [];
|
||||||
|
const seen = new Set();
|
||||||
|
for (const row of [...failedFixRows, ...blockedRows]) {
|
||||||
|
const key = String(row.record.cluster_id ?? "");
|
||||||
|
if (seen.has(key)) continue;
|
||||||
|
seen.add(key);
|
||||||
|
rows.push({
|
||||||
|
record: row.record,
|
||||||
|
state: row.action.status === "failed" ? "fix failed" : `${row.action.action ?? "action"} ${row.action.status}`,
|
||||||
|
reason: row.action.reason ?? row.record.summary ?? "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const record of latest) {
|
||||||
|
const key = String(record.cluster_id ?? "");
|
||||||
|
if (seen.has(key) || (record.needs_human ?? []).length === 0) continue;
|
||||||
|
seen.add(key);
|
||||||
|
rows.push({
|
||||||
|
record,
|
||||||
|
state: "needs human",
|
||||||
|
reason: (record.needs_human ?? []).join("; "),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rows.sort(
|
||||||
|
(a, b) => Date.parse(b.record.published_at ?? "") - Date.parse(a.record.published_at ?? ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockedReasonRows(rows) {
|
||||||
|
const byReason = new Map();
|
||||||
|
for (const row of rows) {
|
||||||
|
const reason = truncate(row.action.reason ?? "unspecified", 140);
|
||||||
|
const current = byReason.get(reason) ?? { count: 0, row };
|
||||||
|
current.count += 1;
|
||||||
|
byReason.set(reason, current);
|
||||||
|
}
|
||||||
|
return [...byReason.entries()]
|
||||||
|
.sort((a, b) => b[1].count - a[1].count)
|
||||||
|
.slice(0, 15)
|
||||||
|
.map(([reason, value]) => `| ${reason} | ${value.count} | ${clusterLink(value.row.record)} |`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newestActionFirst(left, right) {
|
||||||
|
const leftTime = Date.parse(left.action.closed_at ?? left.action.merged_at ?? left.record.published_at ?? "");
|
||||||
|
const rightTime = Date.parse(right.action.closed_at ?? right.action.merged_at ?? right.record.published_at ?? "");
|
||||||
|
return rightTime - leftTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clusterLink(record) {
|
||||||
|
const owner = String(record.repo ?? "unknown/unknown").split("/")[0] || "unknown";
|
||||||
|
const cluster = String(record.cluster_id ?? "");
|
||||||
|
const slug = cluster.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
||||||
|
return link(cluster || "unknown", `${REPORT_REPO}/blob/main/results/${owner}/${slug}.md`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runLink(record) {
|
||||||
|
return record.run_url ? link(record.run_id ?? "run", record.run_url) : "_none_";
|
||||||
|
}
|
||||||
|
|
||||||
|
function targetLink(action) {
|
||||||
|
const target = String(action.target ?? "");
|
||||||
|
const match = target.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/(issues|pull)\/(\d+)/);
|
||||||
|
if (match) return link(`#${match[3]}`, target);
|
||||||
|
return target ? link(target, target) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function inspectionRow(row) {
|
||||||
|
return `| ${clusterLink(row.record)} | ${tableCell(row.state)} | ${truncate(row.reason, 150)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixRow(row) {
|
||||||
|
const action = row.action;
|
||||||
|
return `| ${clusterLink(row.record)} | ${tableCell(action.status)} | ${targetLink(action)} | ${tableCell(action.branch ?? action.pr ?? "")} | ${truncate(action.reason, 150)} | ${runLink(row.record)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRow(row) {
|
||||||
|
const action = row.action;
|
||||||
|
return `| ${targetLink(action)} | ${tableCell(action.action)} | ${truncate(action.title ?? "")} | ${formatTimestamp(action.closed_at ?? action.merged_at ?? row.record.published_at)} | ${clusterLink(row.record)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
||||||
|
}
|
||||||
91
scripts/source.mjs
Normal file
91
scripts/source.mjs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
export const PROFILES = [
|
||||||
|
{
|
||||||
|
slug: "openclaw-openclaw",
|
||||||
|
displayName: "OpenClaw",
|
||||||
|
repo: "openclaw/openclaw",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "openclaw-clawhub",
|
||||||
|
displayName: "ClawHub",
|
||||||
|
repo: "openclaw/clawhub",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "openclaw-clawsweeper",
|
||||||
|
displayName: "ClawSweeper",
|
||||||
|
repo: "openclaw/clawsweeper",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function profileForSlug(slug) {
|
||||||
|
return PROFILES.find((profile) => profile.slug === slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readText(file) {
|
||||||
|
return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readJson(file, fallback = null) {
|
||||||
|
if (!fs.existsSync(file)) return fallback;
|
||||||
|
return JSON.parse(fs.readFileSync(file, "utf8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeText(file, text) {
|
||||||
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||||
|
fs.writeFileSync(file, text, "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markdownFiles(dir) {
|
||||||
|
if (!fs.existsSync(dir)) return [];
|
||||||
|
return fs
|
||||||
|
.readdirSync(dir)
|
||||||
|
.filter((name) => name.endsWith(".md"))
|
||||||
|
.sort()
|
||||||
|
.map((name) => path.join(dir, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonFiles(dir) {
|
||||||
|
if (!fs.existsSync(dir)) return [];
|
||||||
|
return fs
|
||||||
|
.readdirSync(dir)
|
||||||
|
.filter((name) => name.endsWith(".json"))
|
||||||
|
.sort()
|
||||||
|
.map((name) => path.join(dir, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseFrontMatter(markdown) {
|
||||||
|
const match = markdown.match(/^---\n([\s\S]*?)\n---/);
|
||||||
|
const body = {};
|
||||||
|
if (!match?.[1]) return body;
|
||||||
|
for (const line of match[1].split(/\r?\n/)) {
|
||||||
|
const pair = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
||||||
|
if (!pair) continue;
|
||||||
|
body[pair[1]] = parseFrontMatterScalar(pair[2]);
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFrontMatterScalar(value) {
|
||||||
|
const trimmed = String(value ?? "").trim();
|
||||||
|
if (!trimmed) return "";
|
||||||
|
try {
|
||||||
|
return JSON.parse(trimmed);
|
||||||
|
} catch {
|
||||||
|
return trimmed.replace(/^"|"$/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function relativePath(root, file) {
|
||||||
|
return path.relative(root, file).split(path.sep).join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function numberFromFile(file) {
|
||||||
|
const match = path.basename(file).match(/(\d+)\.md$/);
|
||||||
|
return match ? Number(match[1]) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newestTimestamp(...values) {
|
||||||
|
return values.filter(Boolean).sort((a, b) => Date.parse(b) - Date.parse(a))[0] ?? "";
|
||||||
|
}
|
||||||
273
scripts/sweep-dashboard.mjs
Normal file
273
scripts/sweep-dashboard.mjs
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { formatTimestamp, link, percent, rowsOrNone, tableCell, truncate } from "./markdown.mjs";
|
||||||
|
import {
|
||||||
|
PROFILES,
|
||||||
|
markdownFiles,
|
||||||
|
newestTimestamp,
|
||||||
|
numberFromFile,
|
||||||
|
parseFrontMatter,
|
||||||
|
profileForSlug,
|
||||||
|
readJson,
|
||||||
|
readText,
|
||||||
|
relativePath,
|
||||||
|
} from "./source.mjs";
|
||||||
|
|
||||||
|
const FRESH_DAYS = 7;
|
||||||
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||||
|
const REPORT_REPO = "https://github.com/openclaw/clawsweeper";
|
||||||
|
|
||||||
|
export function renderSweepDashboard(root) {
|
||||||
|
const snapshots = PROFILES.map((profile) => sweepSnapshot(root, profile));
|
||||||
|
const totals = snapshots.reduce(
|
||||||
|
(acc, snapshot) => {
|
||||||
|
acc.openRecords += snapshot.openRecords;
|
||||||
|
acc.closedRecords += snapshot.closedRecords;
|
||||||
|
acc.fresh += snapshot.fresh;
|
||||||
|
acc.proposedClose += snapshot.proposedClose;
|
||||||
|
acc.workCandidates += snapshot.workCandidates;
|
||||||
|
acc.failedOrStale += snapshot.failedOrStale;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
openRecords: 0,
|
||||||
|
closedRecords: 0,
|
||||||
|
fresh: 0,
|
||||||
|
proposedClose: 0,
|
||||||
|
workCandidates: 0,
|
||||||
|
failedOrStale: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const recent = snapshots
|
||||||
|
.flatMap((snapshot) => snapshot.recent)
|
||||||
|
.sort((a, b) => Date.parse(b.reviewedAt ?? "") - Date.parse(a.reviewedAt ?? ""))
|
||||||
|
.slice(0, 15);
|
||||||
|
const closed = snapshots
|
||||||
|
.flatMap((snapshot) => snapshot.closed)
|
||||||
|
.sort((a, b) => Date.parse(b.closedAt ?? "") - Date.parse(a.closedAt ?? ""))
|
||||||
|
.slice(0, 15);
|
||||||
|
const work = snapshots
|
||||||
|
.flatMap((snapshot) => snapshot.work)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
priorityScore(b.workPriority) - priorityScore(a.workPriority) ||
|
||||||
|
Date.parse(b.reviewedAt ?? "") - Date.parse(a.reviewedAt ?? ""),
|
||||||
|
)
|
||||||
|
.slice(0, 20);
|
||||||
|
const lastSourceUpdate = newestTimestamp(
|
||||||
|
...snapshots.flatMap((snapshot) => [
|
||||||
|
snapshot.status?.updated_at,
|
||||||
|
snapshot.audit?.generatedAt,
|
||||||
|
snapshot.lastReviewAt,
|
||||||
|
snapshot.lastCloseAt,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
return `## Sweep Dashboard
|
||||||
|
|
||||||
|
Last source update: ${formatTimestamp(lastSourceUpdate)}
|
||||||
|
|
||||||
|
### Fleet
|
||||||
|
|
||||||
|
| Metric | Count |
|
||||||
|
| --- | ---: |
|
||||||
|
| Covered repositories | ${snapshots.length} |
|
||||||
|
| Open review records | ${totals.openRecords} |
|
||||||
|
| Archived closed records | ${totals.closedRecords} |
|
||||||
|
| Fresh reviews, ${FRESH_DAYS}d | ${totals.fresh} |
|
||||||
|
| Proposed closes awaiting apply | ${totals.proposedClose} |
|
||||||
|
| Work candidates awaiting promotion | ${totals.workCandidates} |
|
||||||
|
| Failed or stale reviews | ${totals.failedOrStale} |
|
||||||
|
|
||||||
|
### Current Runs
|
||||||
|
|
||||||
|
| Repository | State | Updated | Run |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(snapshots.map(statusRow), 4)}
|
||||||
|
|
||||||
|
### Repositories
|
||||||
|
|
||||||
|
| Repository | Open records | Archived | Fresh | Proposed closes | Work candidates | Failed/stale | Last review | Last close |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |
|
||||||
|
${rowsOrNone(snapshots.map(repositoryRow), 9)}
|
||||||
|
|
||||||
|
### Work Candidates
|
||||||
|
|
||||||
|
| Repository | Item | Title | Priority | Reviewed | Report |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(work.map(workRow), 6)}
|
||||||
|
|
||||||
|
### Recently Closed
|
||||||
|
|
||||||
|
| Repository | Item | Title | Reason | Closed | Report |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(closed.map(closedRow), 6)}
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Recently Reviewed</summary>
|
||||||
|
|
||||||
|
| Repository | Item | Title | Outcome | Status | Reviewed |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
${rowsOrNone(recent.map(recentRow), 6)}
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Audit Health
|
||||||
|
|
||||||
|
| Repository | Status | Last audit | Missing eligible | Stale records | Protected proposed | Scan complete |
|
||||||
|
| --- | --- | --- | ---: | ---: | ---: | --- |
|
||||||
|
${rowsOrNone(snapshots.map(auditRow), 7)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sweepSnapshot(root, profile) {
|
||||||
|
const itemsDir = path.join(root, "records", profile.slug, "items");
|
||||||
|
const closedDir = path.join(root, "records", profile.slug, "closed");
|
||||||
|
const status = readJson(path.join(root, "results", "sweep-status", `${profile.slug}.json`), {
|
||||||
|
state: "Idle",
|
||||||
|
detail: "No workflow status has been published yet.",
|
||||||
|
updated_at: "",
|
||||||
|
run_url: null,
|
||||||
|
});
|
||||||
|
const audit = readJson(path.join(root, "results", "audit", `${profile.slug}.json`), null);
|
||||||
|
const items = markdownFiles(itemsDir).map((file) => recordFromFile(root, file, profile, false));
|
||||||
|
const closedItems = markdownFiles(closedDir).map((file) =>
|
||||||
|
recordFromFile(root, file, profile, true),
|
||||||
|
);
|
||||||
|
const fresh = items.filter(isFresh).length;
|
||||||
|
const proposedClose = items.filter(
|
||||||
|
(item) => isFresh(item) && item.decision === "close" && item.action === "proposed_close",
|
||||||
|
).length;
|
||||||
|
const work = items.filter(
|
||||||
|
(item) =>
|
||||||
|
isFresh(item) &&
|
||||||
|
item.workCandidate === "queue_fix_pr" &&
|
||||||
|
item.workStatus === "candidate",
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
profile,
|
||||||
|
status,
|
||||||
|
audit,
|
||||||
|
openRecords: items.length,
|
||||||
|
closedRecords: closedItems.length,
|
||||||
|
fresh,
|
||||||
|
proposedClose,
|
||||||
|
workCandidates: work.length,
|
||||||
|
failedOrStale: items.filter(
|
||||||
|
(item) => item.reviewStatus === "failed" || item.reviewStatus.startsWith("stale_"),
|
||||||
|
).length,
|
||||||
|
lastReviewAt: newestTimestamp(...items.map((item) => item.reviewedAt)),
|
||||||
|
lastCloseAt: newestTimestamp(...closedItems.map((item) => item.closedAt)),
|
||||||
|
recent: items
|
||||||
|
.filter((item) => item.reviewedAt)
|
||||||
|
.sort((a, b) => Date.parse(b.reviewedAt) - Date.parse(a.reviewedAt))
|
||||||
|
.slice(0, 15),
|
||||||
|
closed: closedItems.filter((item) => item.closedAt).slice(0, 15),
|
||||||
|
work,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordFromFile(root, file, fallbackProfile, archived) {
|
||||||
|
const frontMatter = parseFrontMatter(readText(file));
|
||||||
|
const profile = profileForSlug(path.basename(path.dirname(path.dirname(file)))) ?? fallbackProfile;
|
||||||
|
const repo = String(frontMatter.repository ?? profile.repo);
|
||||||
|
const kind = String(frontMatter.type ?? "issue");
|
||||||
|
const action = String(frontMatter.action_taken ?? "unknown");
|
||||||
|
const currentState = String(frontMatter.current_state ?? "");
|
||||||
|
return {
|
||||||
|
profile,
|
||||||
|
repo,
|
||||||
|
number: numberFromFile(file),
|
||||||
|
kind,
|
||||||
|
title: String(frontMatter.title ?? ""),
|
||||||
|
reviewedAt: String(frontMatter.reviewed_at ?? ""),
|
||||||
|
decision: String(frontMatter.decision ?? "unknown"),
|
||||||
|
action,
|
||||||
|
reviewStatus: String(frontMatter.review_status ?? ""),
|
||||||
|
workCandidate: String(frontMatter.work_candidate ?? "none"),
|
||||||
|
workPriority: String(frontMatter.work_priority ?? "low"),
|
||||||
|
workStatus: String(frontMatter.work_status ?? "none"),
|
||||||
|
closeReason: closeReason(frontMatter, action, currentState),
|
||||||
|
closedAt: dashboardClosedAt(frontMatter, action, currentState),
|
||||||
|
reportPath: relativePath(root, file),
|
||||||
|
archived,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFresh(item) {
|
||||||
|
const reviewedAt = Date.parse(item.reviewedAt);
|
||||||
|
return Number.isFinite(reviewedAt) && Date.now() - reviewedAt < FRESH_DAYS * DAY_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dashboardClosedAt(frontMatter, action, currentState) {
|
||||||
|
if (frontMatter.applied_at) return String(frontMatter.applied_at);
|
||||||
|
if (frontMatter.current_item_closed_at) return String(frontMatter.current_item_closed_at);
|
||||||
|
if (currentState === "closed") return String(frontMatter.reconciled_at ?? "");
|
||||||
|
if (action === "skipped_already_closed") return String(frontMatter.apply_checked_at ?? "");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeReason(frontMatter, action, currentState) {
|
||||||
|
if (action === "closed") return String(frontMatter.close_reason ?? "closed");
|
||||||
|
if (action === "skipped_already_closed") return "already closed before apply";
|
||||||
|
if (currentState === "closed") return "closed externally after review";
|
||||||
|
return String(frontMatter.close_reason ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function priorityScore(priority) {
|
||||||
|
if (priority === "high") return 3;
|
||||||
|
if (priority === "medium") return 2;
|
||||||
|
if (priority === "low") return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repoLink(repo) {
|
||||||
|
return link(repo, `https://github.com/${repo}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemLink(item) {
|
||||||
|
const segment = item.kind === "pull_request" ? "pull" : "issues";
|
||||||
|
return link(`#${item.number}`, `https://github.com/${item.repo}/${segment}/${item.number}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportLink(item) {
|
||||||
|
return link(item.reportPath, `${REPORT_REPO}/blob/main/${item.reportPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusRow(snapshot) {
|
||||||
|
const status = snapshot.status ?? {};
|
||||||
|
const run = status.run_url ? link("run", status.run_url) : "_none_";
|
||||||
|
return `| ${repoLink(snapshot.profile.repo)} | ${tableCell(status.state ?? "Idle")} | ${formatTimestamp(status.updated_at)} | ${run} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repositoryRow(snapshot) {
|
||||||
|
return `| ${repoLink(snapshot.profile.repo)} | ${snapshot.openRecords} | ${snapshot.closedRecords} | ${snapshot.fresh} | ${snapshot.proposedClose} | ${snapshot.workCandidates} | ${snapshot.failedOrStale} | ${formatTimestamp(snapshot.lastReviewAt)} | ${formatTimestamp(snapshot.lastCloseAt)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function workRow(item) {
|
||||||
|
return `| ${repoLink(item.repo)} | ${itemLink(item)} | ${truncate(item.title)} | ${item.workPriority} | ${formatTimestamp(item.reviewedAt)} | ${reportLink(item)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closedRow(item) {
|
||||||
|
return `| ${repoLink(item.repo)} | ${itemLink(item)} | ${truncate(item.title)} | ${tableCell(item.closeReason)} | ${formatTimestamp(item.closedAt)} | ${reportLink(item)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recentRow(item) {
|
||||||
|
return `| ${repoLink(item.repo)} | ${itemLink(item)} | ${truncate(item.title)} | ${item.decision} / ${item.action} | ${item.reviewStatus} | ${formatTimestamp(item.reviewedAt)} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function auditRow(snapshot) {
|
||||||
|
const audit = snapshot.audit;
|
||||||
|
if (!audit) return `| ${repoLink(snapshot.profile.repo)} | _unknown_ | unknown | 0 | 0 | 0 | unknown |`;
|
||||||
|
const counts = audit.counts ?? {};
|
||||||
|
return `| ${repoLink(snapshot.profile.repo)} | ${tableCell(auditStatus(audit))} | ${formatTimestamp(audit.generatedAt)} | ${counts.missingEligibleOpen ?? 0} | ${counts.staleItemRecords ?? 0} | ${counts.protectedProposed ?? 0} | ${audit.scan?.complete ? "yes" : "no"} |`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function auditStatus(audit) {
|
||||||
|
const counts = audit.counts ?? {};
|
||||||
|
if (!audit.scan?.complete) return "scan incomplete";
|
||||||
|
if ((counts.protectedProposed ?? 0) > 0) return "protected proposed closes";
|
||||||
|
if ((counts.duplicateRecords ?? 0) > 0) return "duplicate records";
|
||||||
|
if ((counts.openArchived ?? 0) > 0) return "open archived records";
|
||||||
|
if ((counts.missingEligibleOpen ?? 0) > 0) return "missing records";
|
||||||
|
return "clean";
|
||||||
|
}
|
||||||
10
test/render.test.mjs
Normal file
10
test/render.test.mjs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import test from "node:test";
|
||||||
|
import { formatTimestamp, percent, tableCell } from "../scripts/markdown.mjs";
|
||||||
|
|
||||||
|
test("markdown helpers keep dashboard tables stable", () => {
|
||||||
|
assert.equal(tableCell("a | b\nc"), "a \\| b c");
|
||||||
|
assert.equal(percent(1, 4), "25.0%");
|
||||||
|
assert.equal(percent(1, 0), "0.0%");
|
||||||
|
assert.match(formatTimestamp("2026-05-01T05:37:00.000Z"), /May 1, 2026/);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user