Skip to content

Commit 45fe165

Browse files
authored
Merge branch 'main' into fix/sdk-environments-discovery
2 parents 7d33e3a + 7c13004 commit 45fe165

86 files changed

Lines changed: 4171 additions & 457 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/crabbox/SKILL.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,18 @@ It should include `broker.url`, `broker.token`, and usually `provider: aws`
266266
for owned-cloud lanes. Do not let that config override the OpenClaw default
267267
when Blacksmith proof is requested; pass `--provider blacksmith-testbox`.
268268

269+
### Interactive Desktop / WebVNC
270+
271+
For human WebVNC demos, keep the remote desktop visible and windowed. Do not
272+
fullscreen the remote browser or hide the XFCE panel/window chrome unless the
273+
explicit goal is video/capture output. After launch, verify a screenshot shows
274+
the desktop panel plus browser title bar. If Chrome is fullscreen, toggle it
275+
back with:
276+
277+
```sh
278+
crabbox run --id <lease> --shell -- 'DISPLAY=:99 xdotool search --onlyvisible --class google-chrome windowactivate key F11'
279+
```
280+
269281
## Diagnostics
270282

271283
```sh

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

Lines changed: 42 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,40 @@ jobs:
474474
echo "- Candidate desktop video: \`candidate/discord-status-reactions-tool-only-desktop.mp4\`"
475475
} > "$root/mantis-report.md"
476476
477+
jq -n \
478+
--arg baseline_status "$baseline_status" \
479+
--arg candidate_status "$candidate_status" \
480+
--arg baseline_sha "${{ needs.validate_refs.outputs.baseline_revision }}" \
481+
--arg candidate_sha "${{ needs.validate_refs.outputs.candidate_revision }}" \
482+
'{
483+
schemaVersion: 1,
484+
id: "discord-status-reactions",
485+
title: "Mantis Discord Status Reactions QA",
486+
summary: "Mantis reran Discord status reactions against the known queued-only baseline and the candidate ref. The baseline reproduced the bug, while the candidate showed the expected queued -> thinking -> done reaction sequence.",
487+
scenario: "discord-status-reactions-tool-only",
488+
comparison: {
489+
baseline: { sha: $baseline_sha, expected: "queued-only", status: $baseline_status, reproduced: ($baseline_status == "fail") },
490+
candidate: { sha: $candidate_sha, expected: "queued -> thinking -> done", status: $candidate_status, fixed: ($candidate_status == "pass") },
491+
pass: (($baseline_status == "fail") and ($candidate_status == "pass"))
492+
},
493+
artifacts: [
494+
{ kind: "timeline", lane: "baseline", label: "Baseline queued-only", path: "baseline/discord-status-reactions-tool-only-timeline.png", targetPath: "baseline.png", alt: "Baseline Discord status reaction timeline", width: 420 },
495+
{ kind: "timeline", lane: "candidate", label: "Candidate queued -> thinking -> done", path: "candidate/discord-status-reactions-tool-only-timeline.png", targetPath: "candidate.png", alt: "Candidate Discord status reaction timeline", width: 420 },
496+
{ kind: "desktopScreenshot", lane: "baseline", label: "Baseline desktop/VNC browser", path: "baseline/discord-status-reactions-tool-only-desktop.png", targetPath: "baseline-desktop.png", alt: "Baseline Mantis desktop browser screenshot", width: 420 },
497+
{ kind: "desktopScreenshot", lane: "candidate", label: "Candidate desktop/VNC browser", path: "candidate/discord-status-reactions-tool-only-desktop.png", targetPath: "candidate-desktop.png", alt: "Candidate Mantis desktop browser screenshot", width: 420 },
498+
{ kind: "motionPreview", lane: "baseline", label: "Baseline motion preview", path: "baseline/discord-status-reactions-tool-only-desktop-preview.gif", targetPath: "baseline-desktop-preview.gif", alt: "Animated baseline desktop preview", width: 420, required: false },
499+
{ kind: "motionPreview", lane: "candidate", label: "Candidate motion preview", path: "candidate/discord-status-reactions-tool-only-desktop-preview.gif", targetPath: "candidate-desktop-preview.gif", alt: "Animated candidate desktop preview", width: 420, required: false },
500+
{ kind: "motionClip", lane: "baseline", label: "Baseline change MP4", path: "baseline/discord-status-reactions-tool-only-desktop-change.mp4", targetPath: "baseline-desktop-change.mp4", required: false },
501+
{ kind: "motionClip", lane: "candidate", label: "Candidate change MP4", path: "candidate/discord-status-reactions-tool-only-desktop-change.mp4", targetPath: "candidate-desktop-change.mp4", required: false },
502+
{ kind: "fullVideo", lane: "baseline", label: "Baseline desktop MP4", path: "baseline/discord-status-reactions-tool-only-desktop.mp4", targetPath: "baseline-desktop.mp4" },
503+
{ kind: "fullVideo", lane: "candidate", label: "Candidate desktop MP4", path: "candidate/discord-status-reactions-tool-only-desktop.mp4", targetPath: "candidate-desktop.mp4" },
504+
{ kind: "metadata", lane: "baseline", label: "Baseline preview metadata", path: "baseline/discord-status-reactions-tool-only-desktop-preview.json", targetPath: "baseline-desktop-preview.json", required: false },
505+
{ kind: "metadata", lane: "candidate", label: "Candidate preview metadata", path: "candidate/discord-status-reactions-tool-only-desktop-preview.json", targetPath: "candidate-desktop-preview.json", required: false },
506+
{ kind: "metadata", lane: "run", label: "Comparison JSON", path: "comparison.json", targetPath: "comparison.json" },
507+
{ kind: "report", lane: "run", label: "Mantis report", path: "mantis-report.md", targetPath: "mantis-report.md" }
508+
]
509+
}' > "$root/mantis-evidence.json"
510+
477511
cat "$root/mantis-report.md" >> "$GITHUB_STEP_SUMMARY"
478512
479513
if [[ "$baseline_status" != "fail" ]]; then
@@ -514,155 +548,17 @@ jobs:
514548
GH_TOKEN: ${{ steps.mantis_app_token.outputs.token }}
515549
TARGET_PR: ${{ needs.resolve_request.outputs.pr_number }}
516550
ARTIFACT_URL: ${{ steps.upload_artifact.outputs.artifact-url }}
517-
BASELINE_SHA: ${{ needs.validate_refs.outputs.baseline_revision }}
518-
CANDIDATE_SHA: ${{ needs.validate_refs.outputs.candidate_revision }}
519551
REQUEST_SOURCE: ${{ needs.resolve_request.outputs.request_source }}
520552
shell: bash
521553
run: |
522554
set -euo pipefail
523555
524-
if [[ ! "$TARGET_PR" =~ ^[0-9]+$ ]]; then
525-
echo "pr_number must be numeric, got '${TARGET_PR}'." >&2
526-
exit 1
527-
fi
528-
529556
root=".artifacts/qa-e2e/mantis/discord-status-reactions"
530-
for required in \
531-
"$root/comparison.json" \
532-
"$root/baseline/discord-status-reactions-tool-only-timeline.png" \
533-
"$root/candidate/discord-status-reactions-tool-only-timeline.png" \
534-
"$root/baseline/discord-status-reactions-tool-only-desktop.png" \
535-
"$root/candidate/discord-status-reactions-tool-only-desktop.png" \
536-
"$root/baseline/discord-status-reactions-tool-only-desktop.mp4" \
537-
"$root/candidate/discord-status-reactions-tool-only-desktop.mp4"
538-
do
539-
if [[ ! -f "$required" ]]; then
540-
echo "Missing required QA evidence file: $required" >&2
541-
exit 1
542-
fi
543-
done
544-
545-
gh api "repos/${GITHUB_REPOSITORY}/pulls/${TARGET_PR}" --jq '.number' >/dev/null
546-
547-
artifact_root="mantis/discord-status-reactions/pr-${TARGET_PR}/run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
548-
artifacts_worktree="$(mktemp -d)"
549-
git init --quiet "$artifacts_worktree"
550-
git -C "$artifacts_worktree" config user.name "github-actions[bot]"
551-
git -C "$artifacts_worktree" config user.email "41898282+github-actions[bot]@users.noreply.github.com"
552-
git -C "$artifacts_worktree" remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
553-
554-
if git -C "$artifacts_worktree" fetch --quiet origin qa-artifacts; then
555-
git -C "$artifacts_worktree" checkout --quiet -B qa-artifacts FETCH_HEAD
556-
else
557-
git -C "$artifacts_worktree" checkout --quiet --orphan qa-artifacts
558-
fi
559-
560-
mkdir -p "$artifacts_worktree/$artifact_root"
561-
cp "$root/baseline/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/baseline.png"
562-
cp "$root/candidate/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/candidate.png"
563-
cp "$root/baseline/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/baseline-desktop.png"
564-
cp "$root/candidate/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/candidate-desktop.png"
565-
has_desktop_previews="false"
566-
if [[ -f "$root/baseline/discord-status-reactions-tool-only-desktop-preview.gif" && -f "$root/candidate/discord-status-reactions-tool-only-desktop-preview.gif" ]]; then
567-
cp "$root/baseline/discord-status-reactions-tool-only-desktop-preview.gif" "$artifacts_worktree/$artifact_root/baseline-desktop-preview.gif"
568-
cp "$root/candidate/discord-status-reactions-tool-only-desktop-preview.gif" "$artifacts_worktree/$artifact_root/candidate-desktop-preview.gif"
569-
cp "$root/baseline/discord-status-reactions-tool-only-desktop-preview.json" "$artifacts_worktree/$artifact_root/baseline-desktop-preview.json"
570-
cp "$root/candidate/discord-status-reactions-tool-only-desktop-preview.json" "$artifacts_worktree/$artifact_root/candidate-desktop-preview.json"
571-
has_desktop_previews="true"
572-
fi
573-
has_change_clips="false"
574-
if [[ -f "$root/baseline/discord-status-reactions-tool-only-desktop-change.mp4" && -f "$root/candidate/discord-status-reactions-tool-only-desktop-change.mp4" ]]; then
575-
cp "$root/baseline/discord-status-reactions-tool-only-desktop-change.mp4" "$artifacts_worktree/$artifact_root/baseline-desktop-change.mp4"
576-
cp "$root/candidate/discord-status-reactions-tool-only-desktop-change.mp4" "$artifacts_worktree/$artifact_root/candidate-desktop-change.mp4"
577-
has_change_clips="true"
578-
fi
579-
cp "$root/baseline/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/baseline-desktop.mp4"
580-
cp "$root/candidate/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/candidate-desktop.mp4"
581-
cp "$root/comparison.json" "$artifacts_worktree/$artifact_root/comparison.json"
582-
cp "$root/mantis-report.md" "$artifacts_worktree/$artifact_root/mantis-report.md"
583-
584-
git -C "$artifacts_worktree" add "$artifact_root"
585-
if git -C "$artifacts_worktree" diff --cached --quiet; then
586-
echo "No QA screenshot/video artifact changes to publish."
587-
else
588-
git -C "$artifacts_worktree" commit --quiet -m "qa: publish Mantis Discord evidence for PR ${TARGET_PR}"
589-
git -C "$artifacts_worktree" push --quiet origin HEAD:qa-artifacts
590-
fi
591-
592-
encoded_artifact_root="${artifact_root// /%20}"
593-
raw_base="https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/qa-artifacts/${encoded_artifact_root}"
594-
baseline_status="$(jq -r '.baseline.status' "$root/comparison.json")"
595-
candidate_status="$(jq -r '.candidate.status' "$root/comparison.json")"
596-
pass="$(jq -r '.pass' "$root/comparison.json")"
597-
preview_section=""
598-
if [[ "$has_desktop_previews" == "true" ]]; then
599-
preview_section="$(cat <<EOF
600-
601-
| Baseline motion preview | Candidate motion preview |
602-
| --- | --- |
603-
| <img src="${raw_base}/baseline-desktop-preview.gif" width="420" alt="Animated baseline desktop preview"> | <img src="${raw_base}/candidate-desktop-preview.gif" width="420" alt="Animated candidate desktop preview"> |
604-
EOF
605-
)"
606-
fi
607-
change_clip_section=""
608-
if [[ "$has_change_clips" == "true" ]]; then
609-
change_clip_section="$(cat <<EOF
610-
611-
Motion-trimmed clips:
612-
- [Baseline change MP4](${raw_base}/baseline-desktop-change.mp4)
613-
- [Candidate change MP4](${raw_base}/candidate-desktop-change.mp4)
614-
EOF
615-
)"
616-
fi
617-
comment_file="$(mktemp)"
618-
cat > "$comment_file" <<EOF
619-
<!-- mantis-discord-status-reactions -->
620-
## Mantis Discord Status Reactions QA
621-
622-
Summary: Mantis reran Discord status reactions against the known queued-only baseline and the candidate ref. The baseline reproduced the bug, while the candidate showed the expected queued -> thinking -> done reaction sequence.
623-
624-
- Scenario: \`discord-status-reactions-tool-only\`
625-
- Trigger: \`${REQUEST_SOURCE}\`
626-
- Run: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}
627-
- Artifact: ${ARTIFACT_URL}
628-
- Baseline: \`${baseline_status}\` at \`${BASELINE_SHA}\`
629-
- Candidate: \`${candidate_status}\` at \`${CANDIDATE_SHA}\`
630-
- Overall: \`${pass}\`
631-
632-
| Baseline queued-only | Candidate queued -> thinking -> done |
633-
| --- | --- |
634-
| <img src="${raw_base}/baseline.png" width="420" alt="Baseline Discord status reaction timeline"> | <img src="${raw_base}/candidate.png" width="420" alt="Candidate Discord status reaction timeline"> |
635-
636-
| Baseline desktop/VNC browser | Candidate desktop/VNC browser |
637-
| --- | --- |
638-
| <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"> |
639-
${preview_section}
640-
${change_clip_section}
641-
642-
Full videos:
643-
- [Baseline desktop MP4](${raw_base}/baseline-desktop.mp4)
644-
- [Candidate desktop MP4](${raw_base}/candidate-desktop.mp4)
645-
646-
Raw QA files: https://github.com/${GITHUB_REPOSITORY}/tree/qa-artifacts/${artifact_root}
647-
EOF
648-
649-
comment_id="$(
650-
gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${TARGET_PR}/comments" \
651-
--jq '.[] | select(.body | contains("<!-- mantis-discord-status-reactions -->")) | .id' \
652-
| tail -n 1
653-
)"
654-
655-
if [[ -n "$comment_id" ]]; then
656-
comment_payload="$(mktemp)"
657-
jq -n --rawfile body "$comment_file" '{ body: $body }' > "$comment_payload"
658-
if gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" --input "$comment_payload" >/dev/null; then
659-
echo "Updated Mantis QA evidence comment on PR #${TARGET_PR}."
660-
else
661-
echo "::warning::Could not update existing Mantis QA evidence comment ${comment_id}; creating a new one."
662-
gh pr comment "$TARGET_PR" --body-file "$comment_file"
663-
echo "Created Mantis QA evidence comment on PR #${TARGET_PR}."
664-
fi
665-
else
666-
gh pr comment "$TARGET_PR" --body-file "$comment_file"
667-
echo "Created Mantis QA evidence comment on PR #${TARGET_PR}."
668-
fi
557+
node scripts/mantis/publish-pr-evidence.mjs \
558+
--manifest "$root/mantis-evidence.json" \
559+
--target-pr "$TARGET_PR" \
560+
--artifact-root "mantis/discord-status-reactions/pr-${TARGET_PR}/run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
561+
--marker "<!-- mantis-discord-status-reactions -->" \
562+
--artifact-url "$ARTIFACT_URL" \
563+
--run-url "https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
564+
--request-source "$REQUEST_SOURCE"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Mantis Scenario
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
scenario_id:
7+
description: Mantis scenario id to run
8+
required: true
9+
default: discord-status-reactions-tool-only
10+
type: choice
11+
options:
12+
- discord-status-reactions-tool-only
13+
- slack-desktop-smoke
14+
baseline_ref:
15+
description: Optional baseline ref for before/after scenarios
16+
required: false
17+
default: 0bf06e953fdda290799fc9fb9244a8f67fdae593
18+
type: string
19+
candidate_ref:
20+
description: Candidate ref, tag, or SHA
21+
required: true
22+
default: main
23+
type: string
24+
pr_number:
25+
description: Optional PR number to receive QA evidence
26+
required: false
27+
type: string
28+
29+
permissions:
30+
actions: write
31+
contents: read
32+
33+
concurrency:
34+
group: mantis-scenario-${{ inputs.scenario_id }}-${{ inputs.pr_number || inputs.candidate_ref || github.run_id }}
35+
cancel-in-progress: false
36+
37+
jobs:
38+
dispatch:
39+
name: Dispatch selected Mantis workflow
40+
runs-on: blacksmith-8vcpu-ubuntu-2404
41+
steps:
42+
- name: Dispatch scenario
43+
env:
44+
GH_TOKEN: ${{ github.token }}
45+
BASELINE_REF: ${{ inputs.baseline_ref }}
46+
CANDIDATE_REF: ${{ inputs.candidate_ref }}
47+
PR_NUMBER: ${{ inputs.pr_number }}
48+
SCENARIO_ID: ${{ inputs.scenario_id }}
49+
shell: bash
50+
run: |
51+
set -euo pipefail
52+
53+
case "$SCENARIO_ID" in
54+
discord-status-reactions-tool-only)
55+
args=(
56+
workflow run mantis-discord-status-reactions.yml
57+
--repo "$GITHUB_REPOSITORY"
58+
--ref main
59+
-f "baseline_ref=${BASELINE_REF}"
60+
-f "candidate_ref=${CANDIDATE_REF}"
61+
)
62+
if [[ -n "${PR_NUMBER:-}" ]]; then
63+
args+=(-f "pr_number=${PR_NUMBER}")
64+
fi
65+
gh "${args[@]}"
66+
;;
67+
slack-desktop-smoke)
68+
args=(
69+
workflow run mantis-slack-desktop-smoke.yml
70+
--repo "$GITHUB_REPOSITORY"
71+
--ref main
72+
-f "candidate_ref=${CANDIDATE_REF}"
73+
)
74+
if [[ -n "${PR_NUMBER:-}" ]]; then
75+
args+=(-f "pr_number=${PR_NUMBER}")
76+
fi
77+
gh "${args[@]}"
78+
;;
79+
*)
80+
echo "Unsupported Mantis scenario: ${SCENARIO_ID}" >&2
81+
exit 1
82+
;;
83+
esac

0 commit comments

Comments
 (0)