Skip to content

Commit 40a3b62

Browse files
authored
Merge branch 'main' into fix/issue-74378
2 parents b0440bd + 24bd0b2 commit 40a3b62

195 files changed

Lines changed: 8413 additions & 717 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/openclaw-pr-maintainer/SKILL.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ gitcrawl search openclaw/openclaw --query "<scope or title keywords>" --mode hyb
2424
gitcrawl cluster-detail openclaw/openclaw --id <cluster-id> --member-limit 20 --body-chars 280 --json
2525
```
2626

27+
## Surface opener identity
28+
29+
- For every reviewed, triaged, closed, or landed issue/PR, show the opener's human name when available, GitHub login, and account age.
30+
- Get the login from `gh issue view` / `gh pr view` (`author.login`), then fetch profile metadata once with `gh api users/<login> --jq '{login,name,created_at,type}'`.
31+
- Report account age as created date plus rough age, for example `Opened by Jane Doe (@jane, account created 2021-04-03, ~5y old)`.
32+
- Also show recent GitHub activity when it informs maintainer risk: OpenClaw PRs, issues, and commits in the last 12 months; for linked issue-fixing PRs, include both the PR author and issue opener when they differ.
33+
- Prefer the bundled helper for activity lookups:
34+
35+
```bash
36+
.agents/skills/openclaw-pr-maintainer/scripts/github-activity.sh <login> [other-login...]
37+
.agents/skills/openclaw-pr-maintainer/scripts/github-activity.sh --global <login>
38+
```
39+
40+
- The helper reports repo-local activity first and can fetch public GitHub contribution totals for the same window with `--global`.
41+
- Report activity compactly, for example `OpenClaw last 12mo: 4 PRs, 2 issues, 11 commits; GitHub public last 12mo: 86 commits, 9 PRs, 3 issues, 12 reviews`.
42+
- If `name` is empty, use the login only. If profile lookup is rate-limited or unavailable, say `account age unknown` rather than omitting the opener.
43+
- Use identity and activity as triage signal, not proof by itself: new, low-activity, or bot-like accounts can raise review caution, but code, repro, and CI evidence still decide.
44+
2745
## Apply close and triage labels correctly
2846

2947
- If an issue or PR matches an auto-close reason, apply the label and let `.github/workflows/auto-response.yml` handle the comment/close/lock flow.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
repo="openclaw/openclaw"
5+
months="12"
6+
include_global="0"
7+
8+
usage() {
9+
printf 'Usage: %s [--repo owner/repo] [--months N] [--global] <github-login> [login...]\n' "$0"
10+
}
11+
12+
die() {
13+
printf 'error: %s\n' "$*" >&2
14+
exit 1
15+
}
16+
17+
need() {
18+
command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
19+
}
20+
21+
date_utc_relative_months() {
22+
local count="$1"
23+
if date -u -v-"${count}"m +%Y-%m-%dT%H:%M:%SZ >/dev/null 2>&1; then
24+
date -u -v-"${count}"m +%Y-%m-%dT%H:%M:%SZ
25+
return
26+
fi
27+
date -u -d "${count} months ago" +%Y-%m-%dT%H:%M:%SZ
28+
}
29+
30+
date_to_epoch() {
31+
local value="$1"
32+
if date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$value" +%s >/dev/null 2>&1; then
33+
date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$value" +%s
34+
return
35+
fi
36+
date -u -d "$value" +%s
37+
}
38+
39+
rough_age() {
40+
local created_at="$1"
41+
local now_s created_s days
42+
now_s=$(date -u +%s)
43+
created_s=$(date_to_epoch "$created_at")
44+
days=$(( (now_s - created_s) / 86400 ))
45+
if (( days < 120 )); then
46+
printf '~%dd old' "$days"
47+
return
48+
fi
49+
awk -v days="$days" 'BEGIN { printf "~%.1fy old", days / 365.2425 }'
50+
}
51+
52+
count_threads() {
53+
local kind="$1"
54+
local login="$2"
55+
local since_ts="$3"
56+
local kind_filter
57+
if [[ "$kind" == "prs" ]]; then
58+
kind_filter='has("pull_request")'
59+
else
60+
kind_filter='has("pull_request") | not'
61+
fi
62+
gh api --paginate "repos/${repo}/issues?state=all&creator=${login}&since=${since_ts}&per_page=100" \
63+
--jq ".[] | select(.created_at >= \"${since_ts}\") | select(${kind_filter}) | .number" |
64+
wc -l |
65+
tr -d '[:space:]'
66+
}
67+
68+
count_commits() {
69+
local login="$1"
70+
local since_ts="$2"
71+
gh api --paginate "repos/${repo}/commits?author=${login}&since=${since_ts}&per_page=100" \
72+
--jq '.[].sha' | wc -l | tr -d '[:space:]'
73+
}
74+
75+
global_activity() {
76+
local login="$1"
77+
local since_ts="$2"
78+
local now_ts="$3"
79+
# shellcheck disable=SC2016
80+
gh api graphql \
81+
-f login="$login" \
82+
-f from="$since_ts" \
83+
-f to="$now_ts" \
84+
-f query='
85+
query($login: String!, $from: DateTime!, $to: DateTime!) {
86+
user(login: $login) {
87+
contributionsCollection(from: $from, to: $to) {
88+
totalCommitContributions
89+
totalIssueContributions
90+
totalPullRequestContributions
91+
totalPullRequestReviewContributions
92+
}
93+
}
94+
}' \
95+
--jq '.data.user.contributionsCollection // empty'
96+
}
97+
98+
while [[ $# -gt 0 ]]; do
99+
case "$1" in
100+
--repo)
101+
[[ $# -ge 2 ]] || die "--repo requires owner/repo"
102+
repo="$2"
103+
shift 2
104+
;;
105+
--months)
106+
[[ $# -ge 2 ]] || die "--months requires a positive integer"
107+
months="$2"
108+
[[ "$months" =~ ^[0-9]+$ && "$months" != "0" ]] || die "--months must be a positive integer"
109+
shift 2
110+
;;
111+
--global)
112+
include_global="1"
113+
shift
114+
;;
115+
-h|--help)
116+
usage
117+
exit 0
118+
;;
119+
--)
120+
shift
121+
break
122+
;;
123+
-*)
124+
die "unknown option: $1"
125+
;;
126+
*)
127+
break
128+
;;
129+
esac
130+
done
131+
132+
[[ $# -gt 0 ]] || {
133+
usage >&2
134+
exit 2
135+
}
136+
137+
need gh
138+
need jq
139+
140+
since_ts=$(date_utc_relative_months "$months")
141+
now_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
142+
143+
for login in "$@"; do
144+
profile=$(gh api "users/${login}" --jq '{login,name,created_at,type}')
145+
display_login=$(jq -r '.login' <<<"$profile")
146+
name=$(jq -r '.name // empty' <<<"$profile")
147+
created_at=$(jq -r '.created_at' <<<"$profile")
148+
type=$(jq -r '.type' <<<"$profile")
149+
created_day=${created_at%%T*}
150+
151+
prs=$(count_threads prs "$display_login" "$since_ts")
152+
issues=$(count_threads issues "$display_login" "$since_ts")
153+
commits=$(count_commits "$display_login" "$since_ts")
154+
155+
if [[ -n "$name" ]]; then
156+
printf '%s (@%s, %s, account created %s, %s)\n' \
157+
"$name" "$display_login" "$type" "$created_day" "$(rough_age "$created_at")"
158+
else
159+
printf '@%s (%s, account created %s, %s)\n' \
160+
"$display_login" "$type" "$created_day" "$(rough_age "$created_at")"
161+
fi
162+
printf '%s last %smo: %s PRs, %s issues, %s commits\n' "$repo" "$months" "$prs" "$issues" "$commits"
163+
164+
if [[ "$include_global" == "1" ]]; then
165+
if global_json=$(global_activity "$display_login" "$since_ts" "$now_ts" 2>/dev/null); then
166+
if [[ -n "$global_json" ]]; then
167+
global_commits=$(jq -r '.totalCommitContributions' <<<"$global_json")
168+
global_issues=$(jq -r '.totalIssueContributions' <<<"$global_json")
169+
global_prs=$(jq -r '.totalPullRequestContributions' <<<"$global_json")
170+
global_reviews=$(jq -r '.totalPullRequestReviewContributions' <<<"$global_json")
171+
printf 'GitHub public last %smo: %s commits, %s PRs, %s issues, %s reviews\n' \
172+
"$months" "$global_commits" "$global_prs" "$global_issues" "$global_reviews"
173+
else
174+
printf 'GitHub public last %smo: unavailable\n' "$months"
175+
fi
176+
else
177+
printf 'GitHub public last %smo: unavailable\n' "$months"
178+
fi
179+
fi
180+
done

.github/pull_request_template.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ If this PR fixes a plugin beta-release blocker, title it `fix(<plugin-id>): beta
3535
- Related #
3636
- [ ] This PR fixes a bug or regression
3737

38+
## Real behavior proof (required for external PRs)
39+
40+
External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only. Screenshots are encouraged even for CLI, console, text, or log changes; terminal screenshots and copied live output count.
41+
42+
- Behavior or issue addressed:
43+
- Real environment tested:
44+
- Exact steps or command run after this patch:
45+
- Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
46+
- Observed result after fix:
47+
- What was not tested:
48+
- Before evidence (optional but encouraged):
49+
3850
## Root Cause (if applicable)
3951

4052
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.

.github/workflows/auto-response.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
issue_comment:
77
types: [created]
88
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned label automation; trusted base checkout only, no untrusted PR code execution
9-
types: [opened, edited, synchronize, reopened, labeled]
9+
types: [opened, edited, synchronize, reopened, labeled, unlabeled]
1010

1111
env:
1212
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

.github/workflows/mantis-discord-status-reactions.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ jobs:
401401
)
402402
pnpm "${args[@]}"
403403
cp "$desktop_dir/desktop-browser-smoke.png" "$root/$lane/discord-status-reactions-tool-only-desktop.png"
404+
cp "$desktop_dir/desktop-browser-smoke.mp4" "$root/$lane/discord-status-reactions-tool-only-desktop.mp4"
404405
}
405406
406407
capture_desktop_lane baseline
@@ -431,6 +432,8 @@ jobs:
431432
echo "- Candidate screenshot: \`candidate/discord-status-reactions-tool-only-timeline.png\`"
432433
echo "- Baseline desktop screenshot: \`baseline/discord-status-reactions-tool-only-desktop.png\`"
433434
echo "- Candidate desktop screenshot: \`candidate/discord-status-reactions-tool-only-desktop.png\`"
435+
echo "- Baseline desktop video: \`baseline/discord-status-reactions-tool-only-desktop.mp4\`"
436+
echo "- Candidate desktop video: \`candidate/discord-status-reactions-tool-only-desktop.mp4\`"
434437
} > "$root/mantis-report.md"
435438
436439
cat "$root/mantis-report.md" >> "$GITHUB_STEP_SUMMARY"
@@ -491,7 +494,9 @@ jobs:
491494
"$root/baseline/discord-status-reactions-tool-only-timeline.png" \
492495
"$root/candidate/discord-status-reactions-tool-only-timeline.png" \
493496
"$root/baseline/discord-status-reactions-tool-only-desktop.png" \
494-
"$root/candidate/discord-status-reactions-tool-only-desktop.png"
497+
"$root/candidate/discord-status-reactions-tool-only-desktop.png" \
498+
"$root/baseline/discord-status-reactions-tool-only-desktop.mp4" \
499+
"$root/candidate/discord-status-reactions-tool-only-desktop.mp4"
495500
do
496501
if [[ ! -f "$required" ]]; then
497502
echo "Missing required QA evidence file: $required" >&2
@@ -519,14 +524,16 @@ jobs:
519524
cp "$root/candidate/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/candidate.png"
520525
cp "$root/baseline/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/baseline-desktop.png"
521526
cp "$root/candidate/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/candidate-desktop.png"
527+
cp "$root/baseline/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/baseline-desktop.mp4"
528+
cp "$root/candidate/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/candidate-desktop.mp4"
522529
cp "$root/comparison.json" "$artifacts_worktree/$artifact_root/comparison.json"
523530
cp "$root/mantis-report.md" "$artifacts_worktree/$artifact_root/mantis-report.md"
524531
525532
git -C "$artifacts_worktree" add "$artifact_root"
526533
if git -C "$artifacts_worktree" diff --cached --quiet; then
527-
echo "No QA screenshot artifact changes to publish."
534+
echo "No QA screenshot/video artifact changes to publish."
528535
else
529-
git -C "$artifacts_worktree" commit --quiet -m "qa: publish Mantis Discord screenshots for PR ${TARGET_PR}"
536+
git -C "$artifacts_worktree" commit --quiet -m "qa: publish Mantis Discord evidence for PR ${TARGET_PR}"
530537
git -C "$artifacts_worktree" push --quiet origin HEAD:qa-artifacts
531538
fi
532539
@@ -558,6 +565,10 @@ jobs:
558565
| --- | --- |
559566
| <img src="${raw_base}/baseline-desktop.png" width="420" alt="Baseline Mantis desktop browser screenshot"> | <img src="${raw_base}/candidate-desktop.png" width="420" alt="Candidate Mantis desktop browser screenshot"> |
560567
568+
Videos:
569+
- [Baseline desktop MP4](${raw_base}/baseline-desktop.mp4)
570+
- [Candidate desktop MP4](${raw_base}/candidate-desktop.mp4)
571+
561572
Raw QA files: https://github.com/${GITHUB_REPOSITORY}/tree/qa-artifacts/${artifact_root}
562573
EOF
563574

.github/workflows/openclaw-live-and-e2e-checks-reusable.yml

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ on:
3434
default: 1
3535
type: number
3636
published_upgrade_survivor_baseline:
37-
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-migration Docker lane
37+
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-migration Docker lanes
3838
required: false
3939
default: openclaw@latest
4040
type: string
@@ -129,7 +129,7 @@ on:
129129
default: 1
130130
type: number
131131
published_upgrade_survivor_baseline:
132-
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-migration Docker lane
132+
description: Published OpenClaw package baseline for the published-upgrade-survivor/update-restart-auth/update-migration Docker lanes
133133
required: false
134134
default: openclaw@latest
135135
type: string
@@ -861,36 +861,24 @@ jobs:
861861
runs-on: blacksmith-4vcpu-ubuntu-2404
862862
timeout-minutes: 5
863863
outputs:
864-
groups_json: ${{ steps.plan.outputs.groups_json }}
864+
groups_json: ${{ steps.groups.outputs.groups_json }}
865865
steps:
866-
- name: Plan targeted Docker lane groups
867-
id: plan
866+
- name: Checkout trusted release harness
867+
uses: actions/checkout@v6
868+
with:
869+
ref: ${{ github.sha }}
870+
fetch-depth: 1
871+
872+
- name: Build targeted Docker lane groups
873+
id: groups
868874
shell: bash
869875
env:
870876
LANES: ${{ inputs.docker_lanes }}
871877
GROUP_SIZE: ${{ inputs.targeted_docker_lane_group_size }}
878+
OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS: ${{ inputs.published_upgrade_survivor_baselines }}
872879
run: |
873880
set -euo pipefail
874-
groups_json="$(
875-
LANES="$LANES" GROUP_SIZE="$GROUP_SIZE" node <<'NODE'
876-
const lanes = [...new Set(String(process.env.LANES || "").split(/[,\s]+/u).map((lane) => lane.trim()).filter(Boolean))];
877-
if (lanes.length === 0) {
878-
throw new Error("docker_lanes is required when planning targeted Docker lane groups.");
879-
}
880-
const rawGroupSize = Number.parseInt(process.env.GROUP_SIZE || "1", 10);
881-
const groupSize = Number.isFinite(rawGroupSize) && rawGroupSize > 0 ? rawGroupSize : 1;
882-
const sanitize = (lane) => lane.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "targeted";
883-
const groups = [];
884-
for (let index = 0; index < lanes.length; index += groupSize) {
885-
const groupLanes = lanes.slice(index, index + groupSize);
886-
const first = sanitize(groupLanes[0]);
887-
const last = sanitize(groupLanes[groupLanes.length - 1]);
888-
const label = groupLanes.length === 1 ? first : `${first}--${last}`;
889-
groups.push({ label, docker_lanes: groupLanes.join(" ") });
890-
}
891-
process.stdout.write(JSON.stringify(groups));
892-
NODE
893-
)"
881+
groups_json="$(node scripts/plan-targeted-docker-lane-groups.mjs)"
894882
echo "groups_json=${groups_json}" >> "$GITHUB_OUTPUT"
895883
896884
validate_docker_lanes:
@@ -957,7 +945,7 @@ jobs:
957945
OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
958946
OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz
959947
OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC: ${{ inputs.published_upgrade_survivor_baseline }}
960-
OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS: ${{ inputs.published_upgrade_survivor_baselines }}
948+
OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS: ${{ matrix.group.published_upgrade_survivor_baselines || inputs.published_upgrade_survivor_baselines }}
961949
OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS: ${{ inputs.published_upgrade_survivor_scenarios }}
962950
OPENCLAW_SKIP_DOCKER_BUILD: "1"
963951
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
@@ -998,6 +986,7 @@ jobs:
998986
shell: bash
999987
env:
1000988
LANES: ${{ matrix.group.docker_lanes }}
989+
GROUP_LABEL: ${{ matrix.group.label }}
1001990
INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
1002991
INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }}
1003992
run: |
@@ -1017,7 +1006,7 @@ jobs:
10171006
plan_path=".artifacts/docker-tests/targeted-plan.json"
10181007
node .release-harness/scripts/test-docker-all.mjs --plan-json > "$plan_path"
10191008
node .release-harness/scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT"
1020-
suffix="$(printf '%s' "$LANES" | tr ',[:space:]' '-' | tr -cd 'A-Za-z0-9._-' | sed -E 's/-+/-/g; s/^-//; s/-$//')"
1009+
suffix="$(printf '%s' "${GROUP_LABEL:-$LANES}" | tr ',[:space:]' '-' | tr -cd 'A-Za-z0-9._-' | sed -E 's/-+/-/g; s/^-//; s/-$//')"
10211010
echo "artifact_suffix=${suffix:-targeted}" >> "$GITHUB_OUTPUT"
10221011
echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT"
10231012

.github/workflows/openclaw-release-checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,8 @@ jobs:
558558
artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
559559
package_sha256: ${{ needs.prepare_release_package.outputs.package_sha256 }}
560560
suite_profile: custom
561-
docker_lanes: doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update
562-
published_upgrade_survivor_baselines: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'all-since-2026.4.23' || '' }}
561+
docker_lanes: doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor update-restart-auth plugins-offline plugin-update
562+
published_upgrade_survivor_baselines: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'last-stable-4 2026.4.23 2026.5.2 2026.4.15' || '' }}
563563
published_upgrade_survivor_scenarios: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'reported-issues' || '' }}
564564
telegram_mode: mock-openai
565565
telegram_scenarios: telegram-help-command,telegram-commands-command,telegram-tools-compact-command,telegram-whoami-command,telegram-context-command,telegram-current-session-status-tool,telegram-mention-gating

0 commit comments

Comments
 (0)