mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-18 20:32:25 +08:00
Compare commits
1 Commits
qa-delete-
...
codex/spli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dde017027c |
@@ -22,18 +22,15 @@ Use when:
|
||||
- Read dependency docs/source/types when the finding depends on external behavior.
|
||||
- Reject unrealistic edge cases, speculative risks, broad rewrites, and fixes that over-complicate the codebase.
|
||||
- Prefer small fixes at the right ownership boundary; no refactor unless it clearly improves the bug class.
|
||||
- When an accepted finding shows a bug class or repeated pattern, inspect the current PR scope for sibling instances before fixing.
|
||||
- Fix the scoped bug class at once when practical; stop at touched surfaces, owner boundaries, and clear follow-up territory.
|
||||
- Keep going until structured review returns no accepted/actionable findings only while the work remains inside the original task scope.
|
||||
- Keep going until structured review returns no accepted/actionable findings.
|
||||
- If a review-triggered fix changes code, rerun focused tests and rerun the structured review helper.
|
||||
- For security-audit suppression changes, verify accepted findings remain auditable: suppressed findings stay in structured output, active output keeps an unsuppressible suppression notice, and aggregate findings cannot hide unrelated active risk.
|
||||
- Never switch or override the requested review engine/model. If the review hits model capacity, retry the same command a few times with the same engine/model.
|
||||
- Be patient with large bundles. Structured review can take up to 30 minutes while the model call is active, especially with Codex tools or web search.
|
||||
- Treat heartbeat lines like `review still running: ... elapsed=... pid=...` as healthy progress, not a hang. Let the helper continue while heartbeats are advancing. Pass `--stream-engine-output` when live engine text is useful; Codex and Claude filter tool/file chatter, other engines pass raw output through.
|
||||
- Treat heartbeat lines like `review still running: ... elapsed=... pid=...` as healthy progress, not a hang. Let the helper continue while heartbeats are advancing.
|
||||
- Do not kill a review just because it has been quiet for 2-5 minutes, or because it is still running under the 30-minute window. Inspect the process only after missing multiple expected heartbeats, after 30 minutes, or after an obviously failed subprocess; prefer letting the same helper command finish.
|
||||
- Tools are useful in review mode. The helper allows read-only inspection tools and web search by default so reviewers can check dependency contracts, upstream docs, and current behavior.
|
||||
- Security perspective is always included, but it should not cripple legitimate functionality. Report security findings only when the change creates a concrete, actionable risk or removes an important safety check.
|
||||
- For regression provenance, if no blamed PR is traceable, use the blamed commit as the provenance: commit SHA, date, and author username. Do not guess a merger or frame missing PR metadata as a separate finding.
|
||||
- Do not invoke built-in `codex review`, nested reviewers, or reviewer panels from inside the review. The helper builds one bundle, calls one selected engine, validates one structured result, and stops.
|
||||
- Stop as soon as the helper exits 0 with no accepted/actionable findings. Do not run an extra review just to get a nicer "clean" line, a second opinion, or clearer closeout wording.
|
||||
- Treat the helper's successful exit plus absence of actionable findings as the clean review result, even if the underlying Codex CLI output is terse.
|
||||
@@ -43,42 +40,6 @@ Use when:
|
||||
- If Gitcrawl reports a portable manifest mismatch, source/runtime DB health error, or stale portable-store checkout, run `gitcrawl doctor --json` and inspect `source_db_health`, `runtime_db_health`, and `portable_store_status` before falling back to live GitHub.
|
||||
- Do not push just to review. Push only when the user requested push/ship/PR update.
|
||||
|
||||
## Scope Governor
|
||||
|
||||
Autoreview is a closeout gate, not permission to rewrite the task.
|
||||
|
||||
Before the first review, freeze a scope baseline: original request or issue, target branch, intended behavior, owner boundary, changed files, and non-test LOC. For inherited or already-bloated branches, use the intended PR diff as the baseline rather than accepting all existing branch drift.
|
||||
|
||||
Before patching a finding, classify it:
|
||||
|
||||
- **In-scope blocker**: the finding is introduced by the current diff, affects the same owner boundary, and can be fixed without changing the task's contract.
|
||||
- **Follow-up**: the finding is real but belongs to an adjacent bug class, sibling surface, cleanup, or broader hardening track.
|
||||
- **Stop-and-escalate**: the finding requires a new protocol/config/storage/public API contract, a different owner boundary, a release-process change, or a design choice outside the original request.
|
||||
|
||||
Stop patching and report the scope break instead of continuing when:
|
||||
|
||||
- a narrow PR turns into an architecture change, protocol change, migration, or release-process change;
|
||||
- the diff grows past 2x the original files or non-test LOC without explicit approval to expand scope;
|
||||
- two review-triggered patch cycles have not converged; pause and reclassify every remaining finding before another edit;
|
||||
- the best fix is "define the canonical contract first" rather than another local inference layer;
|
||||
- fixing the accepted finding would make the PR no longer describe the same behavior, issue, or owner boundary.
|
||||
|
||||
After the two-cycle pause, continue only when every remaining accepted finding is still an in-scope blocker. Otherwise preserve the useful analysis, identify the smallest safe landed subset if one exists, and open or request a follow-up for the larger fix. Do not keep committing speculative fixes just to satisfy the reviewer.
|
||||
|
||||
Do not stack or push review-triggered fix commits while scope classification or focused proof is unresolved. Keep exploratory edits local until the cycle is proven in scope; if scope breaks, remove them from the landing lane instead of preserving them as branch history.
|
||||
|
||||
Critical exceptions must be explicit: active data loss, crash, broken install/upgrade, release blocker, or concrete security exposure. If the exception is not one of those, it is not critical enough to blow up scope.
|
||||
|
||||
## Release Branches And Release Process
|
||||
|
||||
On release, beta, stable, hotfix, signing, notarization, appcast, package-publish, or release-check work, use freeze discipline even when the branch name is not release-like:
|
||||
|
||||
- Fix only release blockers, failed release infrastructure, exact backports, install/upgrade breakage, data loss, crashes, or concrete security exposure.
|
||||
- Treat non-blocking autoreview findings as follow-ups for `main`, not reasons to broaden the release branch.
|
||||
- Do not introduce new product behavior, config surface, protocol shape, migration, plugin ownership, docs narrative, or process policy unless it directly unblocks the release.
|
||||
- Keep proof tied to the release target: exact branch/ref, failing check or shipped-risk reason, smallest command/proof, and whether the fix must also forward-port to `main`.
|
||||
- If review discovers a real but non-critical design problem during release closeout, stop with a follow-up issue/PR plan; do not use the release branch as the refactor lane.
|
||||
|
||||
## Pick Target
|
||||
|
||||
Dirty local work:
|
||||
@@ -88,9 +49,8 @@ Dirty local work:
|
||||
```
|
||||
|
||||
Use this only when the patch is actually unstaged/staged/untracked in the
|
||||
current checkout. `--mode uncommitted` is accepted as an alias for `--mode local`.
|
||||
For committed, pushed, or PR work, point the helper at the commit
|
||||
or branch diff instead; do not force dirty modes just
|
||||
current checkout. For committed, pushed, or PR work, point the helper at the commit
|
||||
or branch diff instead; do not force `--mode local` / `--uncommitted` just
|
||||
because the helper docs mention dirty work first. A clean local review
|
||||
only proves there is no local patch.
|
||||
|
||||
@@ -138,10 +98,6 @@ Format first if formatting can change line locations. Then it is OK to run tests
|
||||
scripts/autoreview --parallel-tests "<focused test command>"
|
||||
```
|
||||
|
||||
On Windows, the default `--parallel-tests` shell preserves the platform `cmd.exe`
|
||||
semantics used by Python `shell=True`. Use `--parallel-tests-shell powershell`
|
||||
or `--parallel-tests-shell pwsh` when the focused test command is PowerShell-specific.
|
||||
|
||||
Tradeoff: tests may force code changes that stale the review. If tests or review lead to code edits, rerun the affected tests and rerun review until no accepted/actionable findings remain. Once that rerun exits cleanly, stop; do not spend another long review cycle on redundant confirmation.
|
||||
|
||||
## Review Panels
|
||||
@@ -186,22 +142,6 @@ OpenClaw repo-local helper:
|
||||
.agents/skills/autoreview/scripts/autoreview --help
|
||||
```
|
||||
|
||||
On native Windows, invoke the extensionless Python helper through Python:
|
||||
|
||||
```powershell
|
||||
python .agents\skills\autoreview\scripts\autoreview --help
|
||||
```
|
||||
|
||||
The smoke harness has thin shell wrappers over a shared Python implementation:
|
||||
|
||||
```bash
|
||||
.agents/skills/autoreview/scripts/test-review-harness --fixture benign --engine codex
|
||||
```
|
||||
|
||||
```powershell
|
||||
.agents\skills\autoreview\scripts\test-review-harness.ps1 -Fixture benign -Engine codex
|
||||
```
|
||||
|
||||
`agent-scripts` checkout helper:
|
||||
|
||||
```bash
|
||||
@@ -223,19 +163,16 @@ If installed from `agent-scripts`, path is:
|
||||
The helper:
|
||||
|
||||
- chooses dirty local changes first
|
||||
- accepts `--mode uncommitted` as an alias for `--mode local`
|
||||
- otherwise uses current PR base if `gh pr view` works
|
||||
- otherwise uses `origin/main` for non-main branches
|
||||
- supports `--engine codex`, `claude`, `droid`, and `copilot`; default is `AUTOREVIEW_ENGINE` or `codex`; Codex should remain the default when nothing is set
|
||||
- resolves bare `git`, `gh`, reviewer, and PowerShell shell commands from absolute `PATH` entries only, never from the reviewed checkout; explicit relative `--*-bin` paths are resolved from the reviewed repository root
|
||||
- use `--mode commit --commit <ref>` for already-committed work, especially clean `main` after landing
|
||||
- should be left in `--mode auto` or forced to `--mode branch` for PR/branch work; do not force `--mode local` after committing
|
||||
- writes only to stdout unless `--output`, `--json-output`, or live streamed engine stderr is set
|
||||
- supports `--dry-run`, `--parallel-tests`, `--parallel-tests-shell`, `--prompt`, `--prompt-file`, `--dataset`, `--no-tools`, `--no-web-search`, and commit refs
|
||||
- supports `--stream-engine-output` or `AUTOREVIEW_STREAM_ENGINE_OUTPUT=1` for live engine text while preserving structured validation; Codex and Claude hide tool/file event details, emit compact activity summaries, and report usage at turn completion
|
||||
- writes only to stdout unless `--output` or `--json-output` is set
|
||||
- supports `--dry-run`, `--parallel-tests`, `--prompt`, `--prompt-file`, `--dataset`, `--no-tools`, `--no-web-search`, and commit refs
|
||||
- supports opt-in review panels with `--panel` / `--reviewers`, plus per-engine `--model` and `--thinking`
|
||||
- allows read-only tools and web search by default where the selected CLI supports them; forbids nested review in the prompt; Codex is run through `codex exec` with read-only sandbox and structured output
|
||||
- prints `review still running: <engine> elapsed=<seconds>s pid=<pid>` to stderr at long-running intervals while waiting for the selected review engine, unless streamed output or compact Codex activity has been visible recently
|
||||
- prints `review still running: <engine> elapsed=<seconds>s pid=<pid>` to stderr at long-running intervals while waiting for the selected review engine
|
||||
- prints `autoreview clean: no accepted/actionable findings reported` when the selected review command exits 0
|
||||
- exits nonzero when accepted/actionable findings are present
|
||||
|
||||
|
||||
@@ -6,15 +6,13 @@ import concurrent.futures
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
|
||||
ENGINES = ("codex", "claude", "droid", "copilot")
|
||||
@@ -102,18 +100,7 @@ def run_with_heartbeat(
|
||||
input_text: str | None = None,
|
||||
label: str,
|
||||
heartbeat_seconds: int = 60,
|
||||
stream_output: bool = False,
|
||||
stream_display: Callable[[str, str], str | None] | None = None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
if stream_output:
|
||||
return run_with_stream(
|
||||
args,
|
||||
cwd,
|
||||
input_text=input_text,
|
||||
label=label,
|
||||
heartbeat_seconds=heartbeat_seconds,
|
||||
stream_display=stream_display,
|
||||
)
|
||||
started = time.monotonic()
|
||||
proc = subprocess.Popen(
|
||||
args,
|
||||
@@ -137,94 +124,13 @@ def run_with_heartbeat(
|
||||
print(f"review still running: {label} elapsed={elapsed}s pid={proc.pid}", file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def run_with_stream(
|
||||
args: list[str],
|
||||
cwd: Path,
|
||||
*,
|
||||
input_text: str | None,
|
||||
label: str,
|
||||
heartbeat_seconds: int,
|
||||
stream_display: Callable[[str, str], str | None] | None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
started = time.monotonic()
|
||||
proc = subprocess.Popen(
|
||||
args,
|
||||
cwd=cwd,
|
||||
stdin=subprocess.PIPE if input_text is not None else None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
events: queue.Queue[tuple[str, str | None]] = queue.Queue()
|
||||
stdout_parts: list[str] = []
|
||||
stderr_parts: list[str] = []
|
||||
|
||||
def read_stream(name: str, stream: Any) -> None:
|
||||
try:
|
||||
for line in iter(stream.readline, ""):
|
||||
events.put((name, line))
|
||||
finally:
|
||||
events.put((name, None))
|
||||
|
||||
def write_stdin() -> None:
|
||||
if proc.stdin is None or input_text is None:
|
||||
return
|
||||
try:
|
||||
proc.stdin.write(input_text)
|
||||
proc.stdin.close()
|
||||
except BrokenPipeError:
|
||||
return
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=read_stream, args=("stdout", proc.stdout), daemon=True),
|
||||
threading.Thread(target=read_stream, args=("stderr", proc.stderr), daemon=True),
|
||||
]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
stdin_thread = threading.Thread(target=write_stdin, daemon=True)
|
||||
stdin_thread.start()
|
||||
|
||||
open_streams = 2
|
||||
while open_streams:
|
||||
try:
|
||||
name, line = events.get(timeout=heartbeat_seconds)
|
||||
except queue.Empty:
|
||||
elapsed = int(time.monotonic() - started)
|
||||
print(f"review still running: {label} elapsed={elapsed}s pid={proc.pid}", file=sys.stderr, flush=True)
|
||||
continue
|
||||
if line is None:
|
||||
open_streams -= 1
|
||||
continue
|
||||
if name == "stdout":
|
||||
stdout_parts.append(line)
|
||||
else:
|
||||
stderr_parts.append(line)
|
||||
display = stream_display(name, line) if stream_display else line
|
||||
if display:
|
||||
target = sys.stdout if name == "stdout" else sys.stderr
|
||||
target.write(display)
|
||||
target.flush()
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
stdin_thread.join(timeout=1)
|
||||
returncode = proc.wait()
|
||||
return subprocess.CompletedProcess(args, returncode, "".join(stdout_parts), "".join(stderr_parts))
|
||||
|
||||
|
||||
def git(repo: Path, *args: str, check: bool = True) -> str:
|
||||
return run([resolve_command("git", repo), *args], repo, check=check).stdout
|
||||
return run(["git", *args], repo, check=check).stdout
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
start = Path.cwd().resolve()
|
||||
unsafe_root = discover_repo_root(start) or start
|
||||
git_bin = find_command("git", unsafe_root)
|
||||
if not git_bin:
|
||||
raise SystemExit("git executable not found. Install Git or add it to PATH.")
|
||||
result = subprocess.run(
|
||||
[git_bin, "rev-parse", "--show-toplevel"],
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
@@ -234,16 +140,6 @@ def repo_root() -> Path:
|
||||
return Path(result.stdout.strip()).resolve()
|
||||
|
||||
|
||||
def discover_repo_root(start: Path) -> Path | None:
|
||||
current = start
|
||||
while True:
|
||||
if (current / ".git").exists():
|
||||
return current
|
||||
if current.parent == current:
|
||||
return None
|
||||
current = current.parent
|
||||
|
||||
|
||||
def current_branch(repo: Path) -> str:
|
||||
return git(repo, "branch", "--show-current", check=False).strip() or "detached"
|
||||
|
||||
@@ -253,7 +149,6 @@ def is_dirty(repo: Path) -> bool:
|
||||
|
||||
|
||||
def choose_target(repo: Path, mode: str, base_ref: str | None) -> tuple[str, str | None]:
|
||||
mode = "local" if mode == "uncommitted" else mode
|
||||
branch = current_branch(repo)
|
||||
if mode == "local" or (mode == "auto" and is_dirty(repo)):
|
||||
return "local", None
|
||||
@@ -265,70 +160,17 @@ def choose_target(repo: Path, mode: str, base_ref: str | None) -> tuple[str, str
|
||||
|
||||
|
||||
def detect_pr_base(repo: Path) -> str | None:
|
||||
gh_bin = find_command("gh", repo)
|
||||
if not gh_bin:
|
||||
if not shutil_which("gh"):
|
||||
return None
|
||||
result = run([gh_bin, "pr", "view", "--json", "baseRefName", "--jq", ".baseRefName"], repo, check=False)
|
||||
result = run(["gh", "pr", "view", "--json", "baseRefName", "--jq", ".baseRefName"], repo, check=False)
|
||||
base = result.stdout.strip()
|
||||
return f"origin/{base}" if result.returncode == 0 and base else None
|
||||
|
||||
|
||||
def resolve_command(name: str, repo: Path) -> str:
|
||||
resolved = find_command(name, repo)
|
||||
if resolved:
|
||||
return resolved
|
||||
raise SystemExit(f"executable not found: {name}. Install it or pass an explicit trusted path when supported.")
|
||||
|
||||
|
||||
def find_command(name: str, repo: Path) -> str | None:
|
||||
command = Path(name)
|
||||
if has_directory_component(name, command):
|
||||
base = command if command.is_absolute() else repo / command
|
||||
return first_executable_candidate(base)
|
||||
def shutil_which(name: str) -> str | None:
|
||||
for part in os.environ.get("PATH", "").split(os.pathsep):
|
||||
if not part or part == ".":
|
||||
continue
|
||||
path_part = Path(part)
|
||||
if not path_part.is_absolute():
|
||||
continue
|
||||
try:
|
||||
resolved_part = path_part.resolve()
|
||||
resolved_repo = repo.resolve()
|
||||
except OSError:
|
||||
continue
|
||||
if is_within(resolved_part, resolved_repo):
|
||||
continue
|
||||
found = first_executable_candidate(resolved_part / name, reject_root=resolved_repo)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
|
||||
def is_within(path: Path, root: Path) -> bool:
|
||||
return path == root or path.is_relative_to(root)
|
||||
|
||||
|
||||
def has_directory_component(name: str, command: Path) -> bool:
|
||||
separators = [separator for separator in (os.sep, os.altsep) if separator]
|
||||
return command.is_absolute() or bool(command.drive) or any(separator in name for separator in separators)
|
||||
|
||||
|
||||
def first_executable_candidate(path: Path, *, reject_root: Path | None = None) -> str | None:
|
||||
if os.name == "nt" and not path.suffix:
|
||||
extensions = [ext for ext in os.environ.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";") if ext]
|
||||
candidates = [path.with_suffix(ext.lower()) for ext in extensions]
|
||||
candidates.extend(path.with_suffix(ext.upper()) for ext in extensions)
|
||||
candidates.append(path)
|
||||
else:
|
||||
candidates = [path]
|
||||
for candidate in candidates:
|
||||
if candidate.is_file() and os.access(candidate, os.X_OK):
|
||||
if reject_root is not None:
|
||||
try:
|
||||
if is_within(candidate.resolve(), reject_root):
|
||||
continue
|
||||
except OSError:
|
||||
continue
|
||||
candidate = Path(part) / name
|
||||
if candidate.exists() and os.access(candidate, os.X_OK):
|
||||
return str(candidate)
|
||||
return None
|
||||
|
||||
@@ -440,36 +282,8 @@ def load_datasets(args: argparse.Namespace) -> str:
|
||||
return "\n\n".join(chunks)
|
||||
|
||||
|
||||
def review_scope_policy() -> str:
|
||||
return textwrap.dedent(
|
||||
"""
|
||||
Review scope discipline:
|
||||
- This helper is a closeout gate. Do not turn a narrow patch into a broad
|
||||
redesign request.
|
||||
- Report a finding only when this diff introduces or exposes a concrete
|
||||
defect that must be fixed before this target can land.
|
||||
- If the best fix requires a new protocol, config, storage, public API,
|
||||
release process, migration, owner-boundary move, or canonical contract,
|
||||
say that directly in the finding and keep the finding tied to the
|
||||
smallest changed line that proves the current patch is not landable.
|
||||
- Do not ask for sibling-surface hardening, cleanup, refactors, or
|
||||
follow-up architecture work unless the current diff is incorrect
|
||||
without that work.
|
||||
- Prefer the smallest correct pre-merge fix. A broader ideal design is
|
||||
not an actionable finding unless the current patch cannot safely land.
|
||||
- If this is release-branch or release-process work, apply freeze
|
||||
discipline. Report only release blockers, exact backport regressions,
|
||||
install/upgrade breakage, crashes, data loss, concrete security
|
||||
exposure, or release-infrastructure failures. Non-blocking design,
|
||||
cleanup, and hardening concerns belong on main as follow-ups.
|
||||
"""
|
||||
).strip()
|
||||
|
||||
|
||||
def build_prompt(repo: Path, target: str, target_ref: str | None, bundle: str, extra_prompt: str, datasets: str) -> str:
|
||||
target_line = f"{target} {target_ref}" if target_ref else target
|
||||
branch = current_branch(repo)
|
||||
scope_policy = review_scope_policy()
|
||||
return textwrap.dedent(
|
||||
f"""
|
||||
You are a senior code reviewer. Review the provided git change bundle only.
|
||||
@@ -491,11 +305,8 @@ def build_prompt(repo: Path, target: str, target_ref: str | None, bundle: str, e
|
||||
- If there are no actionable findings, return an empty findings array and mark the patch correct.
|
||||
|
||||
Review target: {target_line}
|
||||
Current branch: {branch}
|
||||
Repository: {repo}
|
||||
|
||||
{scope_policy}
|
||||
|
||||
{extra_prompt}
|
||||
|
||||
{datasets}
|
||||
@@ -518,18 +329,16 @@ def run_codex(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
raise SystemExit("--no-tools is not supported by the Codex engine; use --engine claude --no-tools for a no-tools run")
|
||||
schema_path = write_json_temp(SCHEMA)
|
||||
output_path = Path(tempfile.NamedTemporaryFile("w", suffix=".json", delete=False).name)
|
||||
cmd = [resolve_command(args.codex_bin, repo), "--ask-for-approval", "never"]
|
||||
cmd = [args.codex_bin, "--ask-for-approval", "never"]
|
||||
if args.web_search:
|
||||
cmd.append("--search")
|
||||
if args.model:
|
||||
cmd.extend(["--model", args.model])
|
||||
if args.thinking:
|
||||
cmd.extend(["-c", f'model_reasoning_effort="{args.thinking}"'])
|
||||
cmd.append("exec")
|
||||
if args.stream_engine_output:
|
||||
cmd.append("--json")
|
||||
cmd.extend(
|
||||
[
|
||||
"exec",
|
||||
"--ephemeral",
|
||||
"-C",
|
||||
str(repo),
|
||||
@@ -542,14 +351,7 @@ def run_codex(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
"-",
|
||||
]
|
||||
)
|
||||
result = run_with_heartbeat(
|
||||
cmd,
|
||||
repo,
|
||||
input_text=prompt,
|
||||
label="codex",
|
||||
stream_output=args.stream_engine_output,
|
||||
stream_display=CodexStreamDisplay() if args.stream_engine_output else None,
|
||||
)
|
||||
result = run_with_heartbeat(cmd, repo, input_text=prompt, label="codex")
|
||||
try:
|
||||
output = output_path.read_text()
|
||||
finally:
|
||||
@@ -562,11 +364,11 @@ def run_codex(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
|
||||
def run_claude(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
cmd = [
|
||||
resolve_command(args.claude_bin, repo),
|
||||
args.claude_bin,
|
||||
"--print",
|
||||
"--no-session-persistence",
|
||||
"--output-format",
|
||||
"stream-json" if args.stream_engine_output else "json",
|
||||
"json",
|
||||
"--json-schema",
|
||||
json.dumps(SCHEMA),
|
||||
]
|
||||
@@ -574,20 +376,11 @@ def run_claude(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
cmd.extend(["--allowedTools", claude_allowed_tools(args)])
|
||||
else:
|
||||
cmd.extend(["--tools", ""])
|
||||
if args.stream_engine_output:
|
||||
cmd.append("--verbose")
|
||||
if args.model:
|
||||
cmd.extend(["--model", args.model])
|
||||
if args.thinking:
|
||||
cmd.extend(["--effort", args.thinking])
|
||||
result = run_with_heartbeat(
|
||||
cmd,
|
||||
repo,
|
||||
input_text=prompt,
|
||||
label="claude",
|
||||
stream_output=args.stream_engine_output,
|
||||
stream_display=ClaudeStreamDisplay() if args.stream_engine_output else None,
|
||||
)
|
||||
result = run_with_heartbeat(cmd, repo, input_text=prompt, label="claude")
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(f"claude engine failed ({result.returncode})\n{result.stderr or result.stdout}")
|
||||
return result.stdout
|
||||
@@ -599,7 +392,7 @@ def run_droid(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
prompt_path = Path(tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False).name)
|
||||
prompt_path.write_text(prompt)
|
||||
cmd = [
|
||||
resolve_command(args.droid_bin, repo),
|
||||
args.droid_bin,
|
||||
"exec",
|
||||
"--cwd",
|
||||
str(repo),
|
||||
@@ -612,7 +405,7 @@ def run_droid(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
cmd.extend(["--model", args.model])
|
||||
if not args.tools:
|
||||
cmd.extend(["--disabled-tools", "*"])
|
||||
result = run_with_heartbeat(cmd, repo, label="droid", stream_output=args.stream_engine_output)
|
||||
result = run_with_heartbeat(cmd, repo, label="droid")
|
||||
prompt_path.unlink(missing_ok=True)
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(f"droid engine failed ({result.returncode})\n{result.stderr or result.stdout}")
|
||||
@@ -629,7 +422,7 @@ def run_copilot(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
prompt_path.write_text(prompt)
|
||||
os.chmod(prompt_path, 0o600)
|
||||
cmd = [
|
||||
resolve_command(args.copilot_bin, repo),
|
||||
args.copilot_bin,
|
||||
"-C",
|
||||
tempdir,
|
||||
"-p",
|
||||
@@ -637,7 +430,7 @@ def run_copilot(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
"--output-format",
|
||||
"json",
|
||||
"--stream",
|
||||
"on" if args.stream_engine_output else "off",
|
||||
"off",
|
||||
"--no-ask-user",
|
||||
"--disable-builtin-mcps",
|
||||
]
|
||||
@@ -654,142 +447,12 @@ def run_copilot(args: argparse.Namespace, repo: Path, prompt: str) -> str:
|
||||
)
|
||||
if args.web_search:
|
||||
cmd.append("--allow-all-urls")
|
||||
result = run_with_heartbeat(cmd, Path(tempdir), label="copilot", stream_output=args.stream_engine_output)
|
||||
result = run_with_heartbeat(cmd, Path(tempdir), label="copilot")
|
||||
if result.returncode != 0:
|
||||
raise SystemExit(f"copilot engine failed ({result.returncode})\n{result.stderr or result.stdout}")
|
||||
return result.stdout
|
||||
|
||||
|
||||
class CodexStreamDisplay:
|
||||
def __init__(self, *, activity_seconds: int = 20) -> None:
|
||||
self.activity_seconds = activity_seconds
|
||||
self.hidden_events = 0
|
||||
self.last_visible = time.monotonic()
|
||||
|
||||
def __call__(self, name: str, line: str) -> str | None:
|
||||
if name != "stdout":
|
||||
return line
|
||||
try:
|
||||
event = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
return self.visible(line)
|
||||
event_type = event.get("type")
|
||||
if event_type == "thread.started":
|
||||
return self.visible(f"codex thread: {event.get('thread_id', '<unknown>')}\n")
|
||||
if event_type == "turn.started":
|
||||
return self.visible("codex turn started\n")
|
||||
if event_type == "turn.completed":
|
||||
usage = event.get("usage")
|
||||
message = format_codex_usage(usage) + "\n" if isinstance(usage, dict) else "codex turn completed\n"
|
||||
return self.visible(self.flush_hidden() + message)
|
||||
item = event.get("item")
|
||||
if isinstance(item, dict) and item.get("type") == "agent_message" and isinstance(item.get("text"), str):
|
||||
return self.visible(self.flush_hidden() + item["text"].rstrip() + "\n")
|
||||
return self.hidden_activity()
|
||||
|
||||
def hidden_activity(self) -> str | None:
|
||||
self.hidden_events += 1
|
||||
if time.monotonic() - self.last_visible < self.activity_seconds:
|
||||
return None
|
||||
return self.visible(self.flush_hidden())
|
||||
|
||||
def flush_hidden(self) -> str:
|
||||
if not self.hidden_events:
|
||||
return ""
|
||||
count = self.hidden_events
|
||||
self.hidden_events = 0
|
||||
return f"codex activity: {count} hidden tool/status events\n"
|
||||
|
||||
def visible(self, text: str) -> str:
|
||||
self.last_visible = time.monotonic()
|
||||
return text
|
||||
|
||||
|
||||
class ClaudeStreamDisplay:
|
||||
def __init__(self, *, activity_seconds: int = 20) -> None:
|
||||
self.activity_seconds = activity_seconds
|
||||
self.hidden_events = 0
|
||||
self.last_visible = time.monotonic()
|
||||
self.started = False
|
||||
|
||||
def __call__(self, name: str, line: str) -> str | None:
|
||||
if name != "stdout":
|
||||
return line
|
||||
try:
|
||||
event = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
return self.visible(line)
|
||||
event_type = event.get("type")
|
||||
if event_type == "system" and not self.started:
|
||||
self.started = True
|
||||
return self.visible("claude turn started\n")
|
||||
if event_type == "assistant":
|
||||
return self.assistant_message(event)
|
||||
if event_type == "result":
|
||||
return self.visible(self.flush_hidden() + self.result_summary(event))
|
||||
return self.hidden_activity()
|
||||
|
||||
def assistant_message(self, event: dict[str, Any]) -> str | None:
|
||||
message = event.get("message")
|
||||
if not isinstance(message, dict):
|
||||
return self.hidden_activity()
|
||||
chunks: list[str] = []
|
||||
for item in message.get("content", []):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
if item.get("type") == "text" and isinstance(item.get("text"), str):
|
||||
chunks.append(item["text"].rstrip())
|
||||
if chunks:
|
||||
return self.visible(self.flush_hidden() + "\n".join(chunks) + "\n")
|
||||
return self.hidden_activity()
|
||||
|
||||
def result_summary(self, event: dict[str, Any]) -> str:
|
||||
usage = event.get("usage")
|
||||
fields: list[str] = []
|
||||
if isinstance(usage, dict):
|
||||
for key in (
|
||||
"input_tokens",
|
||||
"cache_read_input_tokens",
|
||||
"cache_creation_input_tokens",
|
||||
"output_tokens",
|
||||
):
|
||||
value = usage.get(key)
|
||||
if isinstance(value, int):
|
||||
fields.append(f"{key}={value}")
|
||||
cost = event.get("total_cost_usd")
|
||||
if isinstance(cost, (int, float)) and not isinstance(cost, bool):
|
||||
fields.append(f"cost_usd={cost:.6f}")
|
||||
return "claude usage: " + " ".join(fields) + "\n" if fields else "claude turn completed\n"
|
||||
|
||||
def hidden_activity(self) -> str | None:
|
||||
self.hidden_events += 1
|
||||
if time.monotonic() - self.last_visible < self.activity_seconds:
|
||||
return None
|
||||
return self.visible(self.flush_hidden())
|
||||
|
||||
def flush_hidden(self) -> str:
|
||||
if not self.hidden_events:
|
||||
return ""
|
||||
count = self.hidden_events
|
||||
self.hidden_events = 0
|
||||
return f"claude activity: {count} hidden tool/status events\n"
|
||||
|
||||
def visible(self, text: str) -> str:
|
||||
self.last_visible = time.monotonic()
|
||||
return text
|
||||
|
||||
|
||||
def format_codex_usage(usage: dict[str, Any]) -> str:
|
||||
fields = [
|
||||
"input_tokens",
|
||||
"cached_input_tokens",
|
||||
"output_tokens",
|
||||
"reasoning_output_tokens",
|
||||
]
|
||||
parts = [f"{field}={usage[field]}" for field in fields if isinstance(usage.get(field), int)]
|
||||
return "codex usage: " + " ".join(parts) if parts else "codex usage: unavailable"
|
||||
|
||||
|
||||
def claude_allowed_tools(args: argparse.Namespace) -> str:
|
||||
tools = [tool.strip() for tool in args.claude_allowed_tools.split(",") if tool.strip()]
|
||||
if not args.web_search:
|
||||
@@ -827,7 +490,7 @@ def extract_json(text: str) -> dict[str, Any]:
|
||||
|
||||
|
||||
def extract_json_from_jsonl(text: str) -> dict[str, Any] | None:
|
||||
candidates: list[str | dict[str, Any]] = []
|
||||
candidates: list[str] = []
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
@@ -846,13 +509,7 @@ def extract_json_from_jsonl(text: str) -> dict[str, Any] | None:
|
||||
candidates.append(data["content"])
|
||||
if isinstance(event.get("result"), str):
|
||||
candidates.append(event["result"])
|
||||
if isinstance(event.get("structured_output"), dict):
|
||||
candidates.append(event["structured_output"])
|
||||
for candidate in reversed(candidates):
|
||||
if isinstance(candidate, dict):
|
||||
if "findings" in candidate:
|
||||
return candidate
|
||||
continue
|
||||
parsed = parse_json_candidate(candidate)
|
||||
if isinstance(parsed, dict) and "findings" in parsed:
|
||||
return parsed
|
||||
@@ -976,23 +633,9 @@ def print_report(report: dict[str, Any], *, label: str = "autoreview") -> None:
|
||||
print(report["overall_explanation"])
|
||||
|
||||
|
||||
def start_parallel_tests(command: str, repo: Path, shell_kind: str) -> tuple[subprocess.Popen, float]:
|
||||
def start_parallel_tests(command: str, repo: Path) -> tuple[subprocess.Popen, float]:
|
||||
print(f"tests: {command}")
|
||||
if shell_kind == "default" or shell_kind == "cmd":
|
||||
return subprocess.Popen(command, cwd=repo, shell=True), time.time()
|
||||
if shell_kind == "powershell":
|
||||
powershell = resolve_command("powershell", repo)
|
||||
return subprocess.Popen(
|
||||
[powershell, "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command],
|
||||
cwd=repo,
|
||||
), time.time()
|
||||
if shell_kind == "pwsh":
|
||||
pwsh = resolve_command("pwsh", repo)
|
||||
return subprocess.Popen(
|
||||
[pwsh, "-NoProfile", "-Command", command],
|
||||
cwd=repo,
|
||||
), time.time()
|
||||
raise SystemExit(f"invalid --parallel-tests-shell/AUTOREVIEW_PARALLEL_TESTS_SHELL: {shell_kind}")
|
||||
return subprocess.Popen(command, cwd=repo, shell=True), time.time()
|
||||
|
||||
|
||||
def finish_parallel_tests(proc: subprocess.Popen, started: float) -> int:
|
||||
@@ -1003,7 +646,7 @@ def finish_parallel_tests(proc: subprocess.Popen, started: float) -> int:
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Bundle-driven AI code review.")
|
||||
parser.add_argument("--mode", choices=["auto", "local", "uncommitted", "branch", "commit"], default="auto")
|
||||
parser.add_argument("--mode", choices=["auto", "local", "branch", "commit"], default="auto")
|
||||
parser.add_argument("--base")
|
||||
parser.add_argument("--commit", default="HEAD")
|
||||
parser.add_argument("--engine", choices=ENGINES, default=os.environ.get("AUTOREVIEW_ENGINE", "codex"))
|
||||
@@ -1030,19 +673,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--dataset", action="append", help="Extra evidence file to include in the review bundle.")
|
||||
parser.add_argument("--output", help="Write human output to a file as well as stdout.")
|
||||
parser.add_argument("--json-output", help="Write validated structured review JSON.")
|
||||
parser.add_argument(
|
||||
"--stream-engine-output",
|
||||
action="store_true",
|
||||
default=os.environ.get("AUTOREVIEW_STREAM_ENGINE_OUTPUT") == "1",
|
||||
help="Stream review engine output while preserving buffered output for validation. Codex output is filtered to hide tool/file chatter.",
|
||||
)
|
||||
parser.add_argument("--parallel-tests", help="Run a test command concurrently with review; failure fails the helper.")
|
||||
parser.add_argument(
|
||||
"--parallel-tests-shell",
|
||||
choices=["default", "cmd", "powershell", "pwsh"],
|
||||
default=os.environ.get("AUTOREVIEW_PARALLEL_TESTS_SHELL", "default"),
|
||||
help="Shell for --parallel-tests. Default preserves Python shell=True platform behavior; use powershell or pwsh for PowerShell-specific commands.",
|
||||
)
|
||||
parser.add_argument("--require-finding", action="append", default=[], help="Require finding text to contain this substring.")
|
||||
parser.add_argument("--expect-findings", action="store_true", help="Treat findings as success; for harness acceptance tests.")
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
@@ -1248,7 +879,7 @@ def main() -> int:
|
||||
|
||||
tests_proc: tuple[subprocess.Popen, float] | None = None
|
||||
if args.parallel_tests:
|
||||
tests_proc = start_parallel_tests(args.parallel_tests, repo, args.parallel_tests_shell)
|
||||
tests_proc = start_parallel_tests(args.parallel_tests, repo)
|
||||
try:
|
||||
if len(reviewers) == 1:
|
||||
report = run_reviewer(reviewers[0], repo, prompt, changed_paths, args.require_finding)
|
||||
|
||||
@@ -1,16 +1,176 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: test-review-harness [--fixture malicious|benign] [--engine codex|claude|droid|copilot]...
|
||||
|
||||
Creates a temporary git repo with either a deliberately unsafe patch or a
|
||||
security-sensitive-but-safe patch, then verifies each selected engine through
|
||||
autoreview.
|
||||
Default engines: codex, claude.
|
||||
EOF
|
||||
}
|
||||
|
||||
engines=()
|
||||
fixture=malicious
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--fixture)
|
||||
fixture=${2:-}
|
||||
shift 2
|
||||
;;
|
||||
--engine)
|
||||
engines+=("${2:-}")
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$fixture" in
|
||||
malicious|benign) ;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ ${#engines[@]} -eq 0 ]]; then
|
||||
engines=(codex claude)
|
||||
fi
|
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
harness="$script_dir/test-review-harness.py"
|
||||
repo=$(mktemp -d "${TMPDIR:-/tmp}/autoreview-fixture.XXXXXX")
|
||||
trap 'rm -rf "$repo"' EXIT
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
exec python3 "$harness" "$@"
|
||||
cd "$repo"
|
||||
git init --quiet
|
||||
git config user.name "Review Fixture"
|
||||
git config user.email "review-fixture@example.com"
|
||||
|
||||
if [[ "$fixture" == malicious ]]; then
|
||||
cat > app.js <<'EOF'
|
||||
export function uploadPath(name) {
|
||||
return `uploads/${name.replaceAll("/", "")}`;
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat > app.js <<'EOF'
|
||||
import path from "node:path";
|
||||
|
||||
const uploadsRoot = path.resolve("uploads");
|
||||
|
||||
function safeChildPath(root, name) {
|
||||
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
|
||||
throw new Error("invalid child name");
|
||||
}
|
||||
const resolved = path.resolve(root, name);
|
||||
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function uploadPath(name) {
|
||||
return safeChildPath(uploadsRoot, name);
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if command -v python >/dev/null 2>&1; then
|
||||
exec python "$harness" "$@"
|
||||
git add app.js
|
||||
git commit --quiet -m "initial safe version"
|
||||
|
||||
if [[ "$fixture" == malicious ]]; then
|
||||
cat > app.js <<'EOF'
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
export function uploadPath(name) {
|
||||
return `uploads/${name}`;
|
||||
}
|
||||
|
||||
export function deleteUpload(name) {
|
||||
return execSync(`rm -rf uploads/${name}`);
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name, password: user.password };
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat > app.js <<'EOF'
|
||||
import { execFile } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
const uploadsRoot = path.resolve("uploads");
|
||||
const reposRoot = path.resolve("repos");
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function safeChildPath(root, name) {
|
||||
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
|
||||
throw new Error("invalid child name");
|
||||
}
|
||||
const resolved = path.resolve(root, name);
|
||||
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function repoChildPath(root, name) {
|
||||
if (!/^[A-Za-z0-9._-]+$/.test(name)) throw new Error("invalid repo name");
|
||||
return safeChildPath(root, name);
|
||||
}
|
||||
|
||||
export function uploadPath(name) {
|
||||
return safeChildPath(uploadsRoot, name);
|
||||
}
|
||||
|
||||
export async function repoStatus(repoName) {
|
||||
const { stdout } = await execFileAsync("git", ["status", "--short"], {
|
||||
cwd: repoChildPath(reposRoot, repoName),
|
||||
encoding: "utf8",
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
});
|
||||
return stdout;
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
|
||||
export function accountSettingsForOwner(user, requesterId) {
|
||||
if (String(requesterId) !== String(user.id)) throw new Error("forbidden");
|
||||
return { id: user.id, hasPassword: Boolean(user.passwordHash) };
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Python 3 is required to run test-review-harness." >&2
|
||||
exit 127
|
||||
for engine in "${engines[@]}"; do
|
||||
echo "== $engine =="
|
||||
if [[ "$fixture" == malicious ]]; then
|
||||
"$script_dir/autoreview" \
|
||||
--mode local \
|
||||
--engine "$engine" \
|
||||
--prompt "This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch." \
|
||||
--require-finding "command" \
|
||||
--expect-findings
|
||||
else
|
||||
"$script_dir/autoreview" \
|
||||
--mode local \
|
||||
--engine "$engine" \
|
||||
--prompt "Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('malicious', 'benign')]
|
||||
[string] $Fixture,
|
||||
|
||||
[ValidateSet('codex', 'claude', 'droid', 'copilot')]
|
||||
[string[]] $Engine,
|
||||
|
||||
[Alias('h')]
|
||||
[switch] $Help
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$Harness = Join-Path $PSScriptRoot 'test-review-harness.py'
|
||||
$ForwardedArgs = @()
|
||||
|
||||
if ($Help) {
|
||||
$ForwardedArgs += '--help'
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Fixture')) {
|
||||
$ForwardedArgs += @('--fixture', $Fixture)
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Engine')) {
|
||||
foreach ($SelectedEngine in $Engine) {
|
||||
$ForwardedArgs += @('--engine', $SelectedEngine)
|
||||
}
|
||||
}
|
||||
|
||||
$PyLauncher = Get-Command py -ErrorAction SilentlyContinue
|
||||
if ($null -ne $PyLauncher) {
|
||||
& $PyLauncher.Source -3 $Harness @ForwardedArgs
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
$Python = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($null -ne $Python) {
|
||||
& $Python.Source $Harness @ForwardedArgs
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
|
||||
Write-Error 'Python 3 is required to run test-review-harness.'
|
||||
exit 127
|
||||
@@ -1,215 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import runpy
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ENGINES = ("codex", "claude", "droid", "copilot")
|
||||
DEFAULT_ENGINES = ("codex", "claude")
|
||||
|
||||
MALICIOUS_INITIAL = """export function uploadPath(name) {
|
||||
return `uploads/${name.replaceAll("/", "")}`;
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
"""
|
||||
|
||||
BENIGN_INITIAL = r"""import path from "node:path";
|
||||
|
||||
const uploadsRoot = path.resolve("uploads");
|
||||
|
||||
function safeChildPath(root, name) {
|
||||
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
|
||||
throw new Error("invalid child name");
|
||||
}
|
||||
const resolved = path.resolve(root, name);
|
||||
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function uploadPath(name) {
|
||||
return safeChildPath(uploadsRoot, name);
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
"""
|
||||
|
||||
MALICIOUS_CHANGED = """import { execSync } from "node:child_process";
|
||||
|
||||
export function uploadPath(name) {
|
||||
return `uploads/${name}`;
|
||||
}
|
||||
|
||||
export function deleteUpload(name) {
|
||||
return execSync(`rm -rf uploads/${name}`);
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name, password: user.password };
|
||||
}
|
||||
"""
|
||||
|
||||
BENIGN_CHANGED = r"""import { execFile } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
const uploadsRoot = path.resolve("uploads");
|
||||
const reposRoot = path.resolve("repos");
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function safeChildPath(root, name) {
|
||||
if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
|
||||
throw new Error("invalid child name");
|
||||
}
|
||||
const resolved = path.resolve(root, name);
|
||||
if (!resolved.startsWith(root + path.sep)) throw new Error("path escaped root");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function repoChildPath(root, name) {
|
||||
if (!/^[A-Za-z0-9._-]+$/.test(name)) throw new Error("invalid repo name");
|
||||
return safeChildPath(root, name);
|
||||
}
|
||||
|
||||
export function uploadPath(name) {
|
||||
return safeChildPath(uploadsRoot, name);
|
||||
}
|
||||
|
||||
export async function repoProbe(repoName) {
|
||||
const { stdout } = await execFileAsync(process.execPath, ["--version"], {
|
||||
cwd: repoChildPath(reposRoot, repoName),
|
||||
encoding: "utf8",
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
});
|
||||
return stdout;
|
||||
}
|
||||
|
||||
export function publicUser(user) {
|
||||
return { id: user.id, name: user.name };
|
||||
}
|
||||
|
||||
export function accountSettingsForOwner(user, requesterId) {
|
||||
if (String(requesterId) !== String(user.id)) throw new Error("forbidden");
|
||||
return { id: user.id, hasPassword: Boolean(user.passwordHash) };
|
||||
}
|
||||
"""
|
||||
|
||||
MALICIOUS_PROMPT = "This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch."
|
||||
BENIGN_PROMPT = "Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
|
||||
|
||||
|
||||
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="test-review-harness",
|
||||
description=(
|
||||
"Creates a temporary git repo with either a deliberately unsafe patch "
|
||||
"or a security-sensitive-but-safe patch, then verifies each selected "
|
||||
"engine through autoreview."
|
||||
),
|
||||
epilog="Default engines: codex, claude.",
|
||||
)
|
||||
parser.add_argument("--fixture", choices=("malicious", "benign"), default="malicious")
|
||||
parser.add_argument("--engine", action="append", choices=ENGINES, dest="engines")
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def write_fixture_file(repo: Path, content: str) -> None:
|
||||
with (repo / "app.js").open("w", encoding="utf-8", newline="\n") as handle:
|
||||
handle.write(content)
|
||||
|
||||
|
||||
def run(command: list[str], cwd: Path) -> None:
|
||||
subprocess.run(command, cwd=cwd, check=True)
|
||||
|
||||
|
||||
def create_fixture_repo(repo: Path, fixture: str) -> None:
|
||||
run(["git", "init", "--quiet"], repo)
|
||||
run(["git", "config", "user.name", "Review Fixture"], repo)
|
||||
run(["git", "config", "user.email", "review-fixture@example.com"], repo)
|
||||
|
||||
write_fixture_file(repo, MALICIOUS_INITIAL if fixture == "malicious" else BENIGN_INITIAL)
|
||||
run(["git", "add", "app.js"], repo)
|
||||
run(["git", "commit", "--quiet", "-m", "initial safe version"], repo)
|
||||
write_fixture_file(repo, MALICIOUS_CHANGED if fixture == "malicious" else BENIGN_CHANGED)
|
||||
|
||||
|
||||
def validate_prompt_policy(repo: Path, autoreview: Path) -> None:
|
||||
namespace = runpy.run_path(str(autoreview))
|
||||
prompt = namespace["build_prompt"](repo, "local", None, "fixture diff", "", "")
|
||||
required = (
|
||||
"This helper is a closeout gate.",
|
||||
"Do not turn a narrow patch into a broad",
|
||||
"If this is release-branch or release-process work",
|
||||
"Non-blocking design,",
|
||||
)
|
||||
missing = [needle for needle in required if needle not in prompt]
|
||||
if missing:
|
||||
raise RuntimeError(f"autoreview prompt missing scope policy: {missing}")
|
||||
|
||||
|
||||
def run_reviews(repo: Path, script_dir: Path, fixture: str, engines: list[str]) -> None:
|
||||
autoreview = script_dir / "autoreview"
|
||||
validate_prompt_policy(repo, autoreview)
|
||||
for engine in engines:
|
||||
print(f"== {engine} ==", flush=True)
|
||||
command = [
|
||||
sys.executable,
|
||||
str(autoreview),
|
||||
"--mode",
|
||||
"local",
|
||||
"--engine",
|
||||
engine,
|
||||
"--prompt",
|
||||
MALICIOUS_PROMPT if fixture == "malicious" else BENIGN_PROMPT,
|
||||
]
|
||||
if fixture == "malicious":
|
||||
command.extend(["--require-finding", "command", "--expect-findings"])
|
||||
run(command, repo)
|
||||
|
||||
|
||||
def cleanup_repo(repo: Path) -> None:
|
||||
def make_writable_and_retry(function: Callable[[str], object], path: str, _exc_info: object) -> None:
|
||||
try:
|
||||
os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
|
||||
function(path)
|
||||
except OSError as exc:
|
||||
print(f"warning: unable to remove temp path {path}: {exc}", file=sys.stderr)
|
||||
|
||||
if not repo.exists():
|
||||
return
|
||||
try:
|
||||
shutil.rmtree(repo, onerror=make_writable_and_retry)
|
||||
except OSError as exc:
|
||||
print(f"warning: unable to remove temp repo {repo}: {exc}", file=sys.stderr)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
args = parse_args(argv)
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
engines = args.engines or list(DEFAULT_ENGINES)
|
||||
repo = Path(tempfile.mkdtemp(prefix="autoreview-fixture."))
|
||||
try:
|
||||
create_fixture_repo(repo, args.fixture)
|
||||
run_reviews(repo, script_dir, args.fixture, engines)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return int(exc.returncode or 1)
|
||||
finally:
|
||||
cleanup_repo(repo)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
@@ -1,34 +1,44 @@
|
||||
---
|
||||
name: channel-message-flows
|
||||
description: "Use when running QA Lab channel message flow evidence."
|
||||
description: "Use when previewing local channel message flow fixtures."
|
||||
---
|
||||
|
||||
# Channel Message Flows
|
||||
|
||||
Use this from the OpenClaw repo root to run the QA Lab evidence for Telegram
|
||||
draft/final delivery sequencing. This skill no longer launches a standalone
|
||||
script; the behavior is owned by the QA scenario and its Vitest-backed e2e test.
|
||||
Use this from the OpenClaw repo root to send canned channel preview flows while iterating on message UX. These are real sends/edits/deletes against the configured channel target.
|
||||
|
||||
## QA Scenario
|
||||
## Telegram
|
||||
|
||||
Run the scenario through QA Lab:
|
||||
Native Telegram `sendMessageDraft` tool progress, then a final answer:
|
||||
|
||||
```bash
|
||||
pnpm openclaw qa suite --scenario channel-message-flows
|
||||
node --import tsx scripts/dev/channel-message-flows.ts \
|
||||
--channel telegram \
|
||||
--target <telegram-chat-id> \
|
||||
--flow working-final \
|
||||
--duration-ms 20000
|
||||
```
|
||||
|
||||
Run the focused e2e test directly in a Codex worktree:
|
||||
Thinking preview, then a final answer:
|
||||
|
||||
```bash
|
||||
node scripts/run-vitest.mjs test/e2e/qa-lab/channels/channel-message-flows.e2e.test.ts
|
||||
node --import tsx scripts/dev/channel-message-flows.ts \
|
||||
--channel telegram \
|
||||
--target <telegram-chat-id> \
|
||||
--flow thinking-final
|
||||
```
|
||||
|
||||
## References
|
||||
## Options
|
||||
|
||||
- `qa/scenarios/channels/channel-message-flows.yaml`
|
||||
- `test/e2e/qa-lab/channels/channel-message-flows.e2e.test.ts`
|
||||
- `test/e2e/qa-lab/channels/channel-message-flows-runtime.ts`
|
||||
- `--account <accountId>`: Telegram account id when not using the default.
|
||||
- `--thread-id <id>`: Telegram forum topic/message thread id.
|
||||
- `--delay-ms <ms>`: Override preview update cadence.
|
||||
- `--duration-ms <ms>`: Simulated working duration for `working-final`.
|
||||
- `--final-text <text>`: Override the durable final message.
|
||||
|
||||
The scenario covers `channels.streaming` as primary evidence and records
|
||||
secondary coverage for thread preservation, delivery ordering, and reasoning
|
||||
preview visibility.
|
||||
## Notes
|
||||
|
||||
- `--target` is the numeric Telegram chat id.
|
||||
- `working-final` exercises native Telegram `sendMessageDraft` with static `Working` status and sample tool progress.
|
||||
- `thinking-final` exercises formatted `Thinking` reasoning preview clearing before the final answer.
|
||||
- Only `--channel telegram` is implemented for now.
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
---
|
||||
name: claw-score
|
||||
description: Audit or refresh OpenClaw maturity scorecard docs from root taxonomy, maturity scores, and QA evidence artifacts without using maintainer discrawl data or committed inventory reports.
|
||||
---
|
||||
|
||||
# claw-score
|
||||
|
||||
Use this skill when working on the OpenClaw maturity scorecard in this repo.
|
||||
This is the openclaw-local version of the maintainer `claw-score` workflow:
|
||||
it keeps the taxonomy and scorecard concepts, but excludes discrawl and the old
|
||||
committed `inventory/` report tree.
|
||||
|
||||
## Authority
|
||||
|
||||
This skill owns the operational workflow for:
|
||||
|
||||
- `taxonomy.yaml`
|
||||
- `docs/maturity-scores.yaml`
|
||||
- `docs/maturity-scorecard.md`
|
||||
- `docs/taxonomy.md`
|
||||
- `docs/taxonomy-outline.md`
|
||||
- `scripts/render-maturity-docs.mjs`
|
||||
- `.github/workflows/maturity-scorecard.yml`
|
||||
|
||||
Keep person-specific, maintainer-private, Discord archive, and discrawl facts
|
||||
out of this repo. If a score needs private evidence, use the redacted
|
||||
`qa-evidence.json` artifact shape generated by OpenClaw QA workflows.
|
||||
|
||||
## Source Model
|
||||
|
||||
- `taxonomy.yaml` is the hand-edited source of truth for surfaces, levels,
|
||||
QA profiles, categories, feature coverage IDs, docs refs, LTS overrides, and
|
||||
completeness-instruction paths.
|
||||
- `docs/maturity-scores.yaml` is the aggregate score source committed in this
|
||||
repo. It is the only committed score data; do not add generated inventory
|
||||
directories.
|
||||
- `docs/maturity-scorecard.md`, `docs/taxonomy.md`, and
|
||||
`docs/taxonomy-outline.md` are deterministic docs generated from the root
|
||||
taxonomy and aggregate score source.
|
||||
- `qa-evidence.json` artifacts provide per-run QA scorecard evidence. They can
|
||||
enrich generated artifact docs, but they are not committed as inventory.
|
||||
|
||||
## Commands
|
||||
|
||||
Run from the openclaw repo root.
|
||||
|
||||
Render committed docs:
|
||||
|
||||
```bash
|
||||
pnpm maturity:render
|
||||
```
|
||||
|
||||
Check generated docs are current:
|
||||
|
||||
```bash
|
||||
pnpm maturity:check
|
||||
```
|
||||
|
||||
Render an evidence-enriched docs artifact from downloaded QA artifacts:
|
||||
|
||||
```bash
|
||||
pnpm maturity:render -- --evidence-dir .artifacts/maturity-evidence --output-dir .artifacts/maturity-docs
|
||||
```
|
||||
|
||||
## Scoring Workflow
|
||||
|
||||
When asked to score or refresh a surface:
|
||||
|
||||
1. Read the surface in `taxonomy.yaml`.
|
||||
2. Read the surface completeness rubric under
|
||||
`.agents/skills/claw-score/references/completeness/`.
|
||||
3. Gather public repo evidence from docs, source, tests, and QA scenario
|
||||
metadata.
|
||||
4. Prefer existing `qa-evidence.json` artifacts for executed proof. Do not use
|
||||
discrawl or unredacted private archives.
|
||||
5. Update `docs/maturity-scores.yaml` only when the score change is backed by
|
||||
public or redacted artifact evidence.
|
||||
6. Run `pnpm maturity:render`.
|
||||
7. Run `pnpm maturity:check`.
|
||||
|
||||
For subjective score changes, make the smallest defensible edit and leave the
|
||||
evidence path in the PR or task summary. The deterministic renderer owns
|
||||
Markdown structure; manual prose tweaks belong in taxonomy, score source, or
|
||||
the renderer rather than in generated docs.
|
||||
|
||||
## Default Completeness Process
|
||||
|
||||
Completeness is scored against the intended operator-visible workflow for each
|
||||
category, not against test breadth or implementation quality. The completeness
|
||||
reference files under `references/completeness/` define the category scope and
|
||||
any surface-specific variation from this default process.
|
||||
|
||||
By default, Completeness measures how fully OpenClaw exposes the intended
|
||||
surface capability set to the user, operator, author, or maintainer persona for
|
||||
that surface. Score whether each category delivers the full expected workflow,
|
||||
including setup, normal use, status or inspection, recovery, and important
|
||||
platform, provider, channel, security, or lifecycle variants where they apply.
|
||||
|
||||
Treat `Surface-Specific Scoring Questions` and `Surface-Specific Guidance` as
|
||||
higher-priority instructions for that surface. The surface instructions may
|
||||
flesh out, narrow, or intentionally conflict with the default ideas here; when
|
||||
they do, follow the surface instructions and make the score rationale reflect
|
||||
that surface-specific instruction. If a reference file does not include
|
||||
surface-specific questions or guidance, apply this default process to the
|
||||
surface's `Category Scope`.
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can the intended user or operator complete the category workflow end to end?
|
||||
- Are the taxonomy features present as supported capabilities rather than
|
||||
isolated implementation fragments?
|
||||
- Are the important lifecycle stages represented: setup, normal operation,
|
||||
status/inspection, recovery, and upgrade or removal where relevant?
|
||||
- Are the important environment, provider, platform, channel, or security
|
||||
branches present for this surface?
|
||||
- Do the known gaps leave major user-visible capability branches missing?
|
||||
|
||||
Default guidance:
|
||||
|
||||
- Favor higher Completeness when the category supports the full
|
||||
operator-visible workflow described by taxonomy and category evidence.
|
||||
- Lower Completeness when only the happy path exists, when important variants
|
||||
are undocumented or unimplemented, or when recovery/status paths are missing.
|
||||
- Do not lower Completeness because tests are thin; that is Coverage.
|
||||
- Do not lower Completeness because implementation quality is fragile; that is
|
||||
Quality.
|
||||
|
||||
Default Completeness bands:
|
||||
|
||||
- `Lovable` (95-100): complete across expected workflows, variants, and
|
||||
recovery branches, with only minor polish gaps.
|
||||
- `Stable` (80-95): the expected workflow set is broadly present, with only
|
||||
bounded missing branches.
|
||||
- `Beta` (70-80): the main workflow exists, but meaningful branches or recovery
|
||||
paths are still absent.
|
||||
- `Alpha` (50-70): only a partial capability set is present; users can complete
|
||||
some core tasks but not the full expected workflow.
|
||||
- `Experimental` (0-50): the category exposes only fragments of the intended
|
||||
capability.
|
||||
|
||||
## Score Semantics
|
||||
|
||||
- Coverage: public or redacted proof that the feature is exercised by docs,
|
||||
tests, QA scenarios, live lanes, or release evidence.
|
||||
- Quality: reliability, maintainability, operator safety, and regression
|
||||
confidence for the category.
|
||||
- Completeness: how much of the intended operator-visible workflow exists for
|
||||
the category. Use the default completeness process plus any surface-specific
|
||||
variation before changing this score.
|
||||
- LTS: derived from score thresholds and `human_lts_override`; do not hand-edit
|
||||
generated Markdown to change LTS status.
|
||||
|
||||
Bands:
|
||||
|
||||
- `Lovable`: 95-100
|
||||
- `Stable`: 80-95
|
||||
- `Beta`: 70-80
|
||||
- `Alpha`: 50-70
|
||||
- `Experimental`: 0-50
|
||||
|
||||
## GitHub Action
|
||||
|
||||
The `Maturity scorecard` workflow verifies committed generated docs on PRs and
|
||||
pushes. Manual dispatch can also download QA artifacts from another workflow run
|
||||
with `source_run_id` and `artifact_pattern`, render evidence-enriched docs into
|
||||
`.artifacts/maturity-docs`, and upload them as a GitHub artifact.
|
||||
|
||||
Do not add the maintainer repo's `docs/kevinslin/maturity-scorecard/inventory/`
|
||||
tree to openclaw. Those generated reports are intentionally replaced here by
|
||||
short-lived artifact docs and the committed aggregate scorecard pages.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Agent Runtime Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`agent-runtime-and-provider-execution` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Agent Turn Execution: Turn startup and runtime choice, Session and run coordination, Abort and terminal outcomes
|
||||
- External Runtimes and Subagents: External harness selection, CLI runtime aliases, Subagent turns, Runtime recovery
|
||||
- Hosted Provider Execution: Hosted provider turns, Provider-specific model options, Hosted tool use, Reasoning and cache controls, Hosted streaming and replies
|
||||
- Local and Self-hosted Providers: Local provider profiles, Tool-capability flags, Timeouts and context windows, Local smoke checks, Local failure handling
|
||||
- Model and Runtime Selection: Model reference selection, Provider and runtime overrides, Thinking and context settings, Invalid route recovery
|
||||
- Provider Auth: Login and API-key setup, Auth profile selection, Credential health checks, Auth failover, Provider fallback recovery, Rate-limit and capacity recovery, Missing-key and OAuth guidance, Restart and stale-route recovery, Structured provider diagnostics, Subagent credential propagation
|
||||
- Streaming and Progress: Streaming replies, Progress visibility
|
||||
- Tool Calls and Response Handling: Tool-call handling, Usage and response reporting, Failure recovery
|
||||
- Tool Execution Controls: Tool availability rules, Sandboxed exec behavior, Approval flow, Elevated execution, Tool safety controls, Delegated tool access
|
||||
@@ -1,14 +0,0 @@
|
||||
# Android app Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`android-app` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Media Capture: Camera and media capture
|
||||
- Mobile Chat: Chat tab
|
||||
- Connection Setup: Gateway discovery
|
||||
- Distribution: Public Google Play install path, Manual install path, Release smoke and startup performance
|
||||
- Settings: Settings sheet
|
||||
- Voice: Voice tab
|
||||
- Device Runtime: Background reconnect and presence, Device command availability
|
||||
@@ -1,12 +0,0 @@
|
||||
# Anthropic provider path Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`anthropic-provider-path` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Provider Auth and Recovery: API-key onboarding, Claude CLI credential reuse, Setup-token auth, Auth profile health, Model status, Usage windows, Cooldown/profile reporting, Long-context recovery, Fallback guidance
|
||||
- Model and Runtime Selection: Bundled Claude catalog, Canonical anthropic refs, Claude CLI compatibility, Model picker availability, Capability metadata, Runtime selection, Session continuity, MCP/tool bridge, Permission-mode mapping, Fallback prelude
|
||||
- Request Transport and Turn Semantics: API-key/OAuth transport, Messages payloads, Streaming decode, Usage and stop reasons, Abort/error handling, Tool-use blocks, Tool-result replay, Partial JSON recovery, Native thinking, Signed/redacted thinking replay
|
||||
- Prompt Cache and Context: Cache retention, System-prompt cache boundary, 1M context, Fast mode/service tier, Cache diagnostics
|
||||
- Media Inputs: Image input, PDF document input, Media model fallback, Image tool results
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automation: cron, hooks, tasks, polling Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`automation-cron-hooks-tasks-polling` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Cron Jobs: Create/edit/remove jobs, Schedule types, Timezone and stagger, Cron RPCs, Agent cron tool, Manual cron runs, Isolated cron execution, Model/provider preflight, Run history, Timeout and denial diagnostics, Chat announce delivery, Webhook delivery, Failure destinations, Skipped-run alerts, Delivery previews
|
||||
- Event Ingress: Telegram long polling, Telegram webhook mode, Zalo polling/webhook mode, Polling stall diagnostics, iMessage watch fallback, Gmail setup wizard, Watcher start/serve, Tailscale/public routing, Push token validation, Gmail event routing, POST /hooks/wake, POST /hooks/agent, Mapped hooks, Hook auth policy, Async dispatch
|
||||
- Automation Hooks: HOOK.md authoring, Hook discovery, Hook CLI management, Hook packs, Lifecycle event dispatch, api.on registration, Tool-call policy hooks, Message hooks, Session/lifecycle hooks, Plugin approval requests, cron_changed
|
||||
- Background Tasks and Flows: Task list/show/cancel, Task notifications, Task audit and maintenance, Chat task board, Task pressure status, Managed flows, Mirrored flows, openclaw tasks flow, Flow audit and maintenance, Plugin managedFlows
|
||||
- Heartbeat: Heartbeat scheduling, Active hours, Wake and cooldown handling, Due-only heartbeat tasks, Commitment check-ins
|
||||
- Polling Controls: openclaw message poll, Telegram polls, Teams polls, Poll flags, Channel capability gates, process poll, process log, Background process status, No-progress loop detection, Process input controls
|
||||
@@ -1,10 +0,0 @@
|
||||
# Browser automation and exec/sandbox tools Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`browser-automation-and-exec-sandbox-tools` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Browser Automation: Browser Actions, Snapshots, Artifacts, Browser Plugin Service, Profiles, Browser Security, SSRF, Remote Control
|
||||
- Tool Invocation and Execution: Exec Routing, Process Lifecycle, Direct Tool Invoke API, Node System.run, Host Exec Approvals, Elevated Mode
|
||||
- Sandbox and Tool Policy: Sandbox Backends, Workspace Isolation, Sandboxed Browser, Codex Dynamic Tools, Tool Policy, Sandbox Tool Gates
|
||||
@@ -1,14 +0,0 @@
|
||||
# Gateway Web App Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`browser-control-ui-and-webchat` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Browser Realtime Talk: Browser Talk start/stop, Provider session selection, Gateway relay audio, Tool-call consults, Steer and cancel
|
||||
- Browser Access and Trust: Device pairing, Token/password auth, Tailscale Serve auth, Trusted proxy auth, Allowed origins/gatewayUrl
|
||||
- Configuration: Config snapshots, Schema form editing, Raw JSON editing, Base-hash guarded writes, Apply and restart
|
||||
- Browser UI: Gateway-hosted UI, Dashboard open/auth bootstrap, Base-path routing, Static asset recovery, Dev gatewayUrl target, PWA install metadata, Service worker updates, VAPID keys, Subscribe/unsubscribe, Test notifications
|
||||
- WebChat Conversations: Send and abort, Session and agent picker, Model/thinking controls, Attachments, Markdown/tool/media rendering, chat.history projection, chat.send lifecycle, Abort/partial retention, Injected assistant notes, Reconnect continuity, Hosted embeds, External embed gating, Assistant media tickets, Authenticated avatars, CSP image policy
|
||||
- Remote WebChat: macOS WebChat transport, SSH tunnel data plane, Direct ws/wss remote mode, Session continuity, Remote troubleshooting
|
||||
- Operator Console: Health/status/models, Live log tail, Update run/status, Activity summaries, RPC timing telemetry, Channels/login, Session manager and history, Cron, Skills/nodes, Exec approvals/agents
|
||||
@@ -1,15 +0,0 @@
|
||||
# Channel framework Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`channel-framework` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Actions Commands and Approvals: Channel-native commands, Native command session target, Message actions, Message tool API discovery, Channel-native approval prompts
|
||||
- Channel Setup: Supported channel catalog, Channel status taxonomy in channels list, Setup/onboarding flows, Install-on-demand, Setup wizard metadata
|
||||
- Group Thread and Ambient Room Behavior: Group/channel session isolation, Mention-required, Native threads, Broadcast groups, Bot-loop protection
|
||||
- Inbound Access and Identity Gates: DM pairing, Group/channel allowlists, Access group expansion, Mention gating, Sanitized inbound identity/route projections
|
||||
- Media Attachments and Rich Channel Data: Inbound media normalization, Outbound direct text/media sends, Provider-specific channelData, Media roots
|
||||
- Outbound Delivery and Reply Pipeline: Automatic final reply delivery, Durable outbound send orchestration, Reply pipeline transforms, Provider outbound adapter bridge
|
||||
- Conversation Routing and Delivery: Inbound conversation routing, Session key construction, Agent binding precedence, Runtime conversation bindings, Thread/parent-child placement, Plugin registry resolution, Channel account startup, Whole-channel lifecycle controls, Config/secrets reload interactions, Auto-restart
|
||||
- Status Health and Operator Controls: channels.status, Channel health policy, Operator CLI controls, Status read-model
|
||||
@@ -1,12 +0,0 @@
|
||||
# ClawHub Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`clawhub-and-external-plugin-distribution` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Publishing: ClawHub package publishing owner, OpenClaw-owned package release validation for ClawHub, Version bump gates, npm trusted publishing provenance, External code plugin package contract required, Skill package metadata, Skill publishing flow
|
||||
- Catalog Discovery: openclaw plugins search as the ClawHub, Search result metadata, Distinction between plugin search, Catalog lookup failure, Skill catalog search
|
||||
- Compatibility and Trust: openclaw.compat.pluginApi, ClawHub package compatibility validation, npm compatibility fallback to the newest, Official external plugin catalog behavior, Compatibility docs, Operator trust model for installing, ClawHub archive, npm integrity drift, Built-in dangerous-code scanner, ClawHub publishing review/hidden-release behavior as upstream, Skill archive safety, Skill audit signals
|
||||
- Plugin Lifecycle: Source prefixes, Bare package behavior during the launch, Explicit pinned versions, Managed install records that preserve source, Codex, Local, Marketplace list, Supported mapped features, Remote marketplace path safety, Update by plugin id, Reinstall vs update semantics, Downgrade, Uninstall config/index/policy/file cleanup, Gateway restart/reload requirements after, ClawHub skill installs, Skill upload install path, Skill dependency installers
|
||||
- Plugin Health: Per-plugin managed npm project, npm-pack local release-candidate installs, Dependency ownership between plugin packages, Peer dependency relinking, Legacy dependency root cleanup, plugins list, Local plugin index, Troubleshooting stale config, Runtime verification after Gateway
|
||||
@@ -1,37 +0,0 @@
|
||||
# CLI Surface Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`cli-install-update-onboard-doctor` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can a normal operator complete the job end to end from the CLI?
|
||||
- Are the expected environments represented where they matter for the category,
|
||||
such as local installs, remote gateway use, supervised services, or
|
||||
Windows/WSL2?
|
||||
- Are the main lifecycle stages present where relevant: setup, inspection,
|
||||
change, repair, and upgrade?
|
||||
- Are common recovery and troubleshooting branches present, or does the
|
||||
workflow dead-end after the happy path?
|
||||
- Are major documented operator expectations still unimplemented?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness is the CLI operator journey for installation, onboarding, configuration, repair, and upgrade across expected environments and recovery branches.
|
||||
- Score the CLI against the full operator journey, not only installation or the happy path.
|
||||
- Repair, migration, remote, and platform-specific branches are expected where a category exposes them.
|
||||
- For Windows and WSL2, score against the intended supported experience rather than parity with macOS/Linux internals.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- CLI Setup: Installer scripts, Local prefix install, Package-manager installs, Supported Node runtime, Source checkout install, CLI entrypoint
|
||||
- Onboarding and Auth Setup: Guided onboarding, Targeted reconfiguration, Auth choices, Gateway auth storage, Remote onboarding
|
||||
- Plugin and Channel Setup: Channel picker, Plugin install sources, Channel account setup, Post-setup probes, Remote gateway caveat
|
||||
- Gateway Service Management: Foreground gateway runs, Service install and control, Service auth wiring, Drift and reinstall recovery, Service health checks
|
||||
- CLI Observability: Status snapshots, Health snapshots, Remote log tailing, Diagnostics export, Support-safe redaction
|
||||
- Doctor: Interactive repair, Config migration, Auth and SecretRef checks, Plugin validation and repair, Lint and JSON findings, Extra gateway discovery, Supervisor drift repair, Port and startup diagnosis, Runtime path checks, Restart guidance
|
||||
- Updates and Upgrades: Update channels, Install-kind switching, Managed gateway restart, Update status and RPC, Plugin convergence
|
||||
@@ -1,13 +0,0 @@
|
||||
# Discord Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`discord` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Application and bot setup, Token and application ID configuration, Setup wizard and account inspection, Status, doctor, and intent checks, Multi-account bot configuration, Account monitor startup, Gateway WebSocket lifecycle, Reconnect and heartbeat handling, Rate limits and gateway metadata, Status, probe, and health-monitor recovery
|
||||
- Access and Identity: DM policy modes, Allowlist inheritance, Pairing-code approval, Sender authorization, Access-group authorization, Group DM authorization
|
||||
- Conversation Routing and Delivery: Guild and channel admission, Mention gating, Session key isolation, Configured and runtime routing, Inbound context visibility, Forum and media-channel thread posts, Thread actions, Target parsing, Thread context resolution, Thread-bound session routing, ACP agent routing, Routing lifecycle, Discord forum/media channel posts created as, CLI and message-tool thread actions, Discord target parsing for `channel:<id>`, Thread context resolution, Thread-bound session routing for `/focus`, `/unfocus`, `/agents`, `/session idle`, `/session max-age`, `sessions_spawn({ thread, ACP current-conversation bindings and ACP thread, Binding lifecycle behavior, Direct and thread sends, Text chunking and reply mode, Draft and progress edits, Mention and embed rendering, REST retry and final delivery, File uploads, Component file and media-gallery blocks, Video caption follow-up, Voice-message upload, Inbound attachment context
|
||||
- Media and Rich Content: Direct and thread sends, Text chunking and reply mode, Draft and progress edits, Mention and embed rendering, REST retry and final delivery, File uploads, Component file and media-gallery blocks, Video caption follow-up, Voice-message upload, Inbound attachment context, Direct and thread sends, Text chunking and reply mode, Draft and progress edits, Mention and embed rendering, REST retry and final delivery, File uploads, Component file and media-gallery blocks, Video caption follow-up, Voice-message upload, Inbound attachment context, Outbound file uploads from URLs and, Component v2 file and media-gallery blocks, Video caption handling and follow-up media-only delivery, Discord voice-message sends with OGG/Opus conversion, Inbound media/attachment-aware debounce behavior, Realtime voice-channel conversations, General text-only delivery
|
||||
- Native Controls and Approvals: Native slash command registration, Native slash command execution, Model Picker Commands, Components v2 messages, Callback TTL, Native Discord exec/plugin approvals, Sensitive owner-only command routing for prompts, Discord message actions, Action gates under channels.discord.actions.\*
|
||||
- Realtime Voice and Calls: Voice Channel Lifecycle, Auto-join and follow-users, Realtime voice modes, Wake, barge-in, and echo handling, Voice codec and DAVE recovery
|
||||
@@ -1,11 +0,0 @@
|
||||
# Docker / Podman hosting Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`docker-podman-hosting` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Container Setup: Local Image Setup Script, Docker Compose gateway, First-run onboarding, Docker-only first-run notes, Podman setup scripts and Quadlet template, Rootless Podman image setup
|
||||
- Container Operations: Host CLI routing into running Docker/Podman, Container Targeting, Container update/rebuild/restart guidance for Docker, Docker Compose, Gateway token generation, Ownership, Docker Compose, Container health endpoints, Provider/VPS Docker hosting docs, Docker VM persistence/update guidance, Operator-facing update
|
||||
- Image Release and Validation: Root Dockerfile build stages, Docker release workflow, Docker E2E package artifact generation, Docker E2E plan/scheduler scripts, Release-path install
|
||||
- Agent Sandbox and Tooling: Docker gateway setup, Docker-backed agent sandbox support, Container image dependency baking
|
||||
@@ -1,11 +0,0 @@
|
||||
# Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`feishu-qq-bot-wechat-yuanbao-zalo-zalo-personal-regional-channels` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Docs channel index, Official external channel catalog entries, Core channel-plugin catalog, Channel setup wizard, Missing-plugin, Cross-channel ingress/access/refactor concerns, Feishu/Lark bot channel setup, WebSocket default mode, DM pairing, Message delivery, Feishu document, Multi-account credential handling, QQ Open Platform AppID/AppSecret setup, C2C private chat, Group activation, Rich media messages, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode, Bot token, Group policy schema, Text, Status probes, WeChat/Weixin personal messaging, Plugin install, Direct-message pairing, Core-side catalog metadata, External sidecar/helper process behavior, zalouser channel plugin, QR login, DM pairing, Message send, Doctor/status checks for runtime availability, Explicit unofficial-account risk, QQ Open Platform AppID/AppSecret setup and, C2C private chat, Group activation, Inbound and outbound rich media including, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel `openclaw-plugin-yuanbao, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode and optional HTTPS, Bot token, Group policy schema and fail-closed group, Text, Status probes and troubleshooting for token/config/webhook problems, zalouser` channel plugin for Zalo Personal, QR login, DM pairing, Message send, Doctor/status checks for runtime availability and, Explicit unofficial-account risk and operator safeguards
|
||||
- Access and Identity: Feishu/Lark bot channel setup, WebSocket default mode, DM pairing, Message delivery, Feishu document, Multi-account credential handling, QQ Open Platform AppID/AppSecret setup, C2C private chat, Group activation, Rich media messages, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode, Bot token, Group policy schema, Text, Status probes, WeChat/Weixin personal messaging, Plugin install, Direct-message pairing, Core-side catalog metadata, External sidecar/helper process behavior, zalouser channel plugin, QR login, DM pairing, Message send, Doctor/status checks for runtime availability, Explicit unofficial-account risk, QQ Open Platform AppID/AppSecret setup and, C2C private chat, Group activation, Inbound and outbound rich media including, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel `openclaw-plugin-yuanbao, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, zalouser` channel plugin for Zalo Personal, QR login, DM pairing, Message send, Doctor/status checks for runtime availability and, Explicit unofficial-account risk and operator safeguards
|
||||
- Conversation Routing and Delivery: Feishu/Lark bot channel setup, WebSocket default mode, DM pairing, Message delivery, Feishu document, Multi-account credential handling, QQ Open Platform AppID/AppSecret setup, C2C private chat, Group activation, Rich media messages, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode, Bot token, Group policy schema, Text, Status probes, WeChat/Weixin personal messaging, Plugin install, Direct-message pairing, Core-side catalog metadata, External sidecar/helper process behavior, zalouser channel plugin, QR login, DM pairing, Message send, Doctor/status checks for runtime availability, Explicit unofficial-account risk, QQ Open Platform AppID/AppSecret setup and, C2C private chat, Group activation, Inbound and outbound rich media including, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel `openclaw-plugin-yuanbao, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode and optional HTTPS, Bot token, Group policy schema and fail-closed group, Text, Status probes and troubleshooting for token/config/webhook problems, zalouser` channel plugin for Zalo Personal, QR login, DM pairing, Message send, Doctor/status checks for runtime availability and, Explicit unofficial-account risk and operator safeguards
|
||||
- Media and Rich Content: Feishu/Lark bot channel setup, WebSocket default mode, DM pairing, Message delivery, Feishu document, Multi-account credential handling, QQ Open Platform AppID/AppSecret setup, C2C private chat, Group activation, Rich media messages, Slash commands, Multi-account gateway connections, Tencent Yuanbao external channel, AppKey/AppSecret setup, DMs, Outbound queue strategy, Core-side official external catalog, Zalo Bot Creator / Marketplace bot, Long-polling default mode, Bot token, Group policy schema, Text, Status probes, QQ Open Platform AppID/AppSecret setup and, C2C private chat, Group activation, Inbound and outbound rich media including, Slash commands, Multi-account gateway connections, Zalo Bot Creator / Marketplace bot, Long-polling default mode and optional HTTPS, Bot token, Group policy schema and fail-closed group, Text, Status probes and troubleshooting for token/config/webhook problems
|
||||
@@ -1,43 +0,0 @@
|
||||
# Gateway Runtime Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`gateway-runtime` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Does the category cover the main happy path an operator or client needs?
|
||||
- Are the major deployment modes present where they matter for this category:
|
||||
local, remote, node-mediated, supervised, or browser-facing?
|
||||
- Are the main lifecycle stages present where relevant: setup, normal use,
|
||||
status/inspection, and recovery?
|
||||
- Are important security or policy branches present where the category implies
|
||||
them?
|
||||
- Are obvious operator-visible holes or "not yet supported" branches still
|
||||
missing?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness includes operator and connected-client workflows, major deployment modes, and recovery paths, not just gateway protocol capability.
|
||||
- Score the Gateway against the full operator and client journey, not just protocol primitives or one transport path.
|
||||
- Local, remote, node-mediated, supervised, and browser-facing modes matter when the category implies them.
|
||||
- Approval/policy variants and recovery or diagnostic paths count as completeness branches, not polish.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Approvals and Remote Execution: Exec approvals, Plugin approvals, Node exec approvals, Approved node execution, Approval mutation safety, Delivery fallback behavior
|
||||
- HTTP APIs: OpenAI-compatible APIs, Tool invocation API, Admin API access, Hook ingress
|
||||
- Hosted Web Surface: Control UI, WebChat hosting, Plugin web routes, Canvas and A2UI routes
|
||||
- Gateway RPC APIs and Events: Health APIs, Identity and presence APIs, Model APIs, Usage and memory APIs, Session APIs, Chat APIs, Channel APIs, Web login and wake APIs, Config and secrets APIs, Update and setup APIs, Agent and artifact APIs, Task and automation APIs, Tool and skill APIs, Request and event envelopes, Idempotent side effects, Method discovery, Event discovery, Accepted-then-final results, Event ordering, State refresh after gaps
|
||||
- Device Auth and Pairing: Shared-secret login, Trusted proxy auth, Private ingress mode, Device challenge signing, Device tokens, Setup-code bootstrap, Auth mismatch recovery, Device auth migration, Client pairing, Node pairing
|
||||
- Network Access and Discovery: Loopback and LAN access, Tailnet access, SSH tunnels, Endpoint discovery, Saved endpoints, TLS pinning
|
||||
- Nodes and Remote Capabilities: Node presence, Node capabilities, Node inventory, Node actions, Node events, Pending work delivery, Remote device capabilities, Remote host commands
|
||||
- Health, Diagnostics, and Repair: Health snapshots, Channel readiness, Stability diagnostics, Payload diagnostics, Diagnostics exports, Doctor checks, Log tailing
|
||||
- Protocol Compatibility: Published protocol schema, Runtime request validation, JSON Schema export, Swift client models, Version negotiation, Client transport defaults, Backward-compatible evolution
|
||||
- Roles and Permissions: Role negotiation, Operator permissions, Approval-gated actions, Untrusted node declarations, Event scoping
|
||||
- Gateway Lifecycle: Foreground startup, Service installation, Restart and stop, Service status, Bind and port settings, Config reload, Multi-gateway isolation
|
||||
- Security Controls: Non-loopback auth, Trusted proxy exceptions, Gateway and node trust boundaries, Trusted CIDR auto-approval, Fail-closed protocol handling, Remote execution safeguards
|
||||
- WebSocket Connection: WebSocket transport, Connect challenge, Connect request, Protocol version negotiation, hello-ok snapshot, Startup retry, Session limits, Plugin surface URLs
|
||||
@@ -1,12 +0,0 @@
|
||||
# Google Chat Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`google-chat` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Google Cloud project setup, Chat app configuration, Service account setup, Webhook audience and path, Workspace visibility and app status, Guided channel setup, Account resolution, Service account SecretRefs, Env file and inline credentials, Channel status and probes, Directory and mutable-id diagnostics, NPM and ClawHub install, Plugin docs and catalog routing, Channel aliases and labels, Operator status UI, Install/update metadata, Webhook path handling, Standard Chat token verification, Workspace add-on token verification, Audience and appPrincipal validation, Shared-path target selection, Auth rejection diagnostics, Account resolution, Service account SecretRefs, Env file and inline credentials, Channel status and probes, Directory and mutable-id diagnostics, NPM and ClawHub install, Plugin docs and catalog routing, Channel aliases and labels, Operator status UI, Install/update metadata, Webhook path handling, Standard Chat token verification, Workspace add-on token verification, Audience and appPrincipal binding, Shared-path target selection, Auth rejection diagnostics
|
||||
- Access and Identity: DM pairing approval, Sender allowlists, Google Chat identity matching, Direct session routing, Pairing diagnostics, Space allowlists, Mention gating, Sender access groups, Group session isolation, Bot-loop protection, Space diagnostics
|
||||
- Conversation Routing and Delivery: DM pairing approval, Sender allowlists, Google Chat identity matching, Direct session routing, Pairing diagnostics, Space allowlists, Mention gating, Sender access groups, Group session isolation, Bot-loop protection, Space diagnostics, Inbound attachments, Outbound media replies, Message upload action, Media source and size controls, Media receipts and thread placement, Text send action, Upload-file action, Reaction actions, Action capability gates, Approval sender matching, Thread-aware replies, Streaming and chunked replies, Typing placeholder lifecycle, Message-tool current-source replies, NO_REPLY cleanup, Markdown/text rendering, Thread-aware replies, Streaming and chunked replies, Typing placeholder lifecycle, Message-tool current-source replies, NO_REPLY cleanup, Markdown/text rendering
|
||||
- Media and Rich Content: Inbound attachments, Outbound media replies, Message upload action, Media source and size controls, Media receipts and thread placement, Text send action, Upload-file action, Reaction actions, Action capability gates, Approval sender matching, Thread-aware replies, Streaming and chunked replies, Typing placeholder lifecycle, Message-tool current-source replies, NO_REPLY cleanup, Markdown/text rendering
|
||||
- Native Controls and Approvals: Inbound attachments, Outbound media replies, Message upload action, Media source and size controls, Media receipts and thread placement, Text send action, Upload-file action, Reaction actions, Action capability gates, Approval sender matching, Thread-aware replies, Streaming and chunked replies, Typing placeholder lifecycle, Message-tool current-source replies, NO_REPLY cleanup, Markdown/text rendering
|
||||
@@ -1,12 +0,0 @@
|
||||
# Google provider path Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`google-provider-path` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Provider Setup and Credentials: API key onboarding, Auth choice metadata, Gemini CLI OAuth setup, Vertex ADC setup, Daemon and fallback credentials, CLI runtime selection, OAuth login and refresh, Canonical Google model refs, CLI usage normalization, OAuth diagnostics
|
||||
- Model Routing and Endpoints: Catalog rows and aliases, Dynamic model resolution, Provider routing, Google-native config normalization, Model picker availability, Vertex provider selection, ADC/service-account auth, Project/location endpoints, Custom base URL policy, Compatibility boundaries
|
||||
- Direct Gemini Runtime: Direct Gemini chat, Multimodal inputs, Tool-call streaming, Usage and stop reasons, Thought-signature replay, Thinking-level mapping, Thought-signature replay, Tool turn ordering, Incomplete-turn recovery, Planning-only turn recovery
|
||||
- Media, Search, and Realtime: Bundled plugin distribution, Provider auto-enable metadata, Image and media adapters, Speech and realtime adapters, Search and generation tools, Realtime voice sessions, Constrained browser tokens, Audio and transcript events, Live tool calls, Session reconnects
|
||||
- Prompt Caching: Cache retention config, Managed cachedContents, Manual cachedContent handles, Cache usage accounting, Cache diagnostics and live proof
|
||||
@@ -1,12 +0,0 @@
|
||||
# Image/video/music generation tools Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`image-video-music-generation-tools` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Media Routing and Discovery: default media model config, per-call model refs and fallbacks, auth-backed tool discovery, action=list provider inspection
|
||||
- Task Lifecycle and Delivery: background task creation, task status/list/show/cancel, duplicate guards, progress keepalive, completion/failure wake, no-session inline fallback, local media persistence, MIME/filename inference, Hosted URL fallback, message-tool handoff, idempotent missing-media fallback, channel attachment proof
|
||||
- Image Generation: text-to-image, reference-image editing, output hints, action=status, provider attempt metadata, OpenAI/Codex OAuth, API-key OpenAI, OpenRouter/xAI/fal/LiteLLM/DeepInfra/Google/MiniMax/ComfyUI auth, provider error diagnostics
|
||||
- Video Generation: text-to-video, image-to-video, video-to-video, reference role validation, audio refs, typed providerOptions, queue-backed jobs, polling/timeout handling, Hosted URL download, provider skip explanations, returned asset metadata
|
||||
- Music Generation: prompt and lyrics input, instrumental mode, duration/format controls, image-reference edit lanes, generated audio outputs, provider fallback
|
||||
@@ -1,12 +0,0 @@
|
||||
# iMessage / BlueBubbles Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`imessage-bluebubbles` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Translate legacy config, Cut over safely, Handle migration caveats, Run local imsg, Run through SSH wrapper, Grant macOS permissions, Probe runtime health, Account setup prompts, Account status checks, Doctor repair checks, Account Config, Translate legacy config, Cut over safely, Handle migration caveats, Run local imsg, Run through SSH wrapper, Grant macOS permissions, Probe runtime health
|
||||
- Access and Identity: Authorize direct senders, Route direct conversations, Bind ACP sessions, Group Policy, Mentions, System Prompts, Group Policy, Mentions, System Prompts
|
||||
- Conversation Routing and Delivery: Watch live messages, Coalesce split-send DMs, Replay missed messages, Seed conversation history, Authorize direct senders, Route direct conversations, Bind ACP sessions, Group Policy, Mentions, System Prompts
|
||||
- Media and Rich Content: Media, Attachments, Remote Fetch, Chunking, Native Actions, Private API, Message Tool
|
||||
- Native Controls and Approvals: Native Approvals, Reactions, Operator Control, Media, Attachments, Remote Fetch, Chunking, Native Actions, Private API, Message Tool, Native Actions, Private API, Message Tool
|
||||
@@ -1,15 +0,0 @@
|
||||
# iOS app Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`ios-app` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Media and Sharing: Camera list/snap/clip
|
||||
- Canvas and Screen: Canvas present/hide/navigate/eval/snapshot
|
||||
- Chat and Sessions: Chat sessions and operator controls
|
||||
- Gateway Setup and Diagnostics: Bonjour/local, Manual host/port, Gateway connect configuration persistence, TLS fingerprint trust prompt, Pairing approval, Pairing/auth diagnostics for users, Settings tab
|
||||
- Distribution: Internal preview status
|
||||
- Device Commands: Location modes, Device command handling
|
||||
- Notifications and Background: APNs registration and relay delivery
|
||||
- Voice: Voice wake
|
||||
@@ -1,29 +0,0 @@
|
||||
# Kubernetes Hosting Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`kubernetes-hosting` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can an operator deploy and manage OpenClaw on Kubernetes end to end?
|
||||
- Are the taxonomy features present as supported manifests, commands, and docs rather than examples only?
|
||||
- Are setup, normal operation, status or inspection, redeploy, teardown, and secret rotation represented where relevant?
|
||||
- Are local Kind validation, namespace/image customization, provider secrets, and secure exposure branches covered?
|
||||
- Do known gaps leave major cluster-hosting capability branches missing?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness is the Kubernetes operator workflow for deployment, configuration, secrets, access, exposure, lifecycle, security posture, status, and recovery.
|
||||
- A complete Kubernetes category lets an operator deploy, expose, secure, update, troubleshoot, and remove the Gateway without relying on Docker-only assumptions.
|
||||
- Happy-path port-forwarding, missing secret/config rotation, or omitted exposed-service security posture are material completeness gaps.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Deployment Setup: Kustomize packaging, cluster prerequisites, quick deploy, manifest apply, and Kind validation.
|
||||
- Configuration and Secrets: agent instructions, Gateway config, provider secrets, secret rotation, and image/namespace customization.
|
||||
- Access and Exposure: port-forward access, service endpoint, ingress exposure, auth/TLS, and localhost posture.
|
||||
- Cluster Lifecycle: resource layout, state persistence, redeploy, teardown, and security context.
|
||||
@@ -1,12 +0,0 @@
|
||||
# Linux companion app Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`linux-companion-app` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- App Distribution: Native app package, Distro package targets, Official release metadata
|
||||
- Gateway Connectivity: Local Gateway attach and status, Gateway pairing and auth, Remote mode, Local and remote resource boundaries
|
||||
- Chat and Sessions: Native Linux chat window, Transcript, Gateway chat transport
|
||||
- Desktop Capabilities: Linux desktop permissions, Secret storage, Sandbox/package posture, Linux native node identity, Host command execution, Desktop tools, Linux native Talk, Microphone capture, Native media permissions
|
||||
- Status and Diagnostics: Native Linux app readiness, Gateway health/status display, Log/transcript opening, Doctor/repair affordances, Linux tray/status item, Runtime status row, Desktop-environment integration
|
||||
@@ -1,12 +0,0 @@
|
||||
# Linux Gateway host Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`linux-gateway-host` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Host Setup and Updates: Linux CLI install, Node runtime prerequisites, Package-manager policy, Update path
|
||||
- Gateway Runtime and Service Control: Foreground Gateway Runtime, Process Control, Systemd User Service Lifecycle setup, Systemd User Service Lifecycle operation, Systemd User Service Lifecycle status, Systemd User Service Lifecycle recovery
|
||||
- Remote Access and Security: Remote Network Exposure, TLS, Tailscale, Gateway exposure safeguards, Gateway authentication modes, Secret Handling
|
||||
- Diagnostics and Repair: Gateway diagnostic reports, Gateway log tailing, Doctor checks, Operator repair guidance
|
||||
- Deployment Targets: VPS, Container, Cloud Deployment Guidance
|
||||
@@ -1,12 +0,0 @@
|
||||
# Local model providers: Ollama, vLLM, SGLang, LM Studio Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`local-model-providers-ollama-vllm-sglang-lm-studio` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Provider Setup, Lifecycle, and Diagnostics: Provider Selection, Onboarding, localService configuration, Process startup and readiness, Request leases and idle shutdown, Health checks and restart, Provider recipes, Local provider status, Backend reachability probes, Model availability errors, Memory readiness diagnostics, Provider troubleshooting docs
|
||||
- Native Provider Plugins: Ollama setup and model pulling, Model discovery, Streaming and vision, Ollama embeddings, Web-search support, LM Studio setup, Model discovery and auth, Model preload and JIT loading, Streaming compatibility, LM Studio embeddings
|
||||
- OpenAI-Compatible Runtime Compatibility: Bundled provider setup, Model Discovery Endpoint, Non-interactive configuration, vLLM thinking controls, OpenAI-compatible chat and tool semantics, SGLang compatibility guidance, Request Stream Compatibility, Tool Calling
|
||||
- Local Memory and Embeddings: Embedding provider selection, Memory search readiness, memoryFlush model override, Fallback lexical search, Provider mismatch guidance
|
||||
- Network Safety and Prompt Controls: Safety Network, Prompt Pressure Controls
|
||||
@@ -1,10 +0,0 @@
|
||||
# Long-tail hosted providers Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`long-tail-hosted-providers` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Hosted LLM Providers: Bedrock setup, Gateway/proxy routing, Copilot/OpenCode hosted access, Proxy capability diagnostics, Hosted text completion, Tool-call and streaming compatibility, Model catalog resolution, Provider-specific request shaping, Regional provider setup, Region and plan routing, Regional live smoke, Account prerequisite diagnostics
|
||||
- Hosted Media Providers: Image generation providers, Video generation providers, Music generation providers, Media mode coverage, Text-to-speech providers, Speech-to-text providers, Realtime transcription providers, Audio format diagnostics
|
||||
- Provider Operations: Provider directory, Provider install catalog, Model catalog metadata, Catalog parity checks, Provider setup descriptors, Auth profiles and aliases, Credential health probes, Key rotation and recovery, Direct provider smoke, Gateway live smoke, Models status probes, Fallback trace and repair
|
||||
@@ -1,14 +0,0 @@
|
||||
# macOS companion app Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`macos-companion-app` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Canvas: Canvas panel open/hide/navigate/eval/snapshot, Local custom URL scheme, A2UI host auto-navigation, Canvas enable/disable setting
|
||||
- Local Setup: Local mode Gateway attach/start/stop, LaunchAgent install/update/restart/uninstall, Existing-listener detection, Native first-run onboarding flow, CLI discovery, Local workspace selection, Onboarding WebChat session separation
|
||||
- Status and Settings: Menu-bar status, Activity state ingestion, Settings navigation, Health polling, Channels settings
|
||||
- Native Capabilities: Mac node session connection, system.run, Exec approval policy, Permission requests, TCC persistence
|
||||
- Remote Connections: Remote connection mode selection, SSH tunnel, Gateway discovery
|
||||
- Voice and Talk: Voice Wake runtime, Push-to-talk, Talk provider playback plan
|
||||
- WebChat: Native SwiftUI WebChat window, Gateway chat transport, Local and remote data-plane reuse
|
||||
@@ -1,14 +0,0 @@
|
||||
# macOS Gateway host Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`macos-gateway-host` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- CLI Setup: Hosted installer, Node 24 recommendation, App-triggered CLI install, Shell PATH and version-manager drift
|
||||
- Local Gateway Integration: App local/remote connection mode, App-managed Gateway LaunchAgent install/restart/uninstall, CLI install detection, Attach-to-existing local Gateway compatibility, Gateway endpoint, gateway.mode=local configuration, Loopback bind, Local app endpoint resolution, Bonjour discovery
|
||||
- Remote Gateway Mode: macOS app "Remote over SSH", SSH tunnel setup, Tailscale MagicDNS, Remote endpoint token/password/TLS fingerprint, Local node host startup
|
||||
- Gateway Service Lifecycle: Per-user Gateway LaunchAgent install, launchctl bootstrap, LaunchAgent labels, Gateway token/env handling, App-managed LaunchAgent handoff, openclaw update package/git handoff, Managed service refresh, Stale updater launchd job detection, openclaw uninstall, Stranded service recovery
|
||||
- Diagnostics and Observability: LaunchAgent log paths, openclaw gateway status --deep, Gateway silently stops responding, Stale updater jobs
|
||||
- Permissions and Native Capabilities: macOS TCC permission prompts/status, Native node capability exposure, system.run policy, Permission-driven support
|
||||
- Profiles and Isolation: Profile-specific LaunchAgent labels, Profile-specific state/config/workspace roots, Derived ports, Rescue bot setup, Extra Gateway process detection
|
||||
@@ -1,13 +0,0 @@
|
||||
# Matrix Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`matrix` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Matrix plugin identity, Setup wizard, Account discovery, Matrix doctor warnings, Matrix probe/status, Shared Matrix client resolution, Monitor startup, Startup maintenance, Matrix doctor warnings, Matrix probe/status, Monitor startup, Startup maintenance
|
||||
- Access and Identity: DM policy, Direct-room classification, Inbound route selection across sender-bound DMs, Mention gates, Matrix thread reply routing, Persisted Matrix thread routing managers, ACP/subagent spawn hooks
|
||||
- Conversation Routing and Delivery: DM policy, Direct-room classification, Inbound route selection across sender-bound DMs, Mention gates, Matrix thread reply routing, Persisted Matrix thread routing managers, ACP/subagent spawn hooks, Channel action discovery, Message send/read/edit/delete, Profile media loading, Outbound Matrix text, Message presentation metadata, Inbound media failure handling, Message send/read/edit/delete, Profile media loading, Outbound Matrix text, Message presentation metadata, Inbound media failure handling
|
||||
- Media and Rich Content: Channel action discovery, Message send/read/edit/delete, Profile media loading, Outbound Matrix text, Message presentation metadata, Inbound media failure handling
|
||||
- Native Controls and Approvals: Channel action discovery, Message send/read/edit/delete, Profile media loading, Outbound Matrix text, Message presentation metadata, Inbound media failure handling, Matrix native exec, Origin target resolution from Matrix turn, Approver DM target resolution, Matrix approval metadata, Origin target resolution from Matrix turn, Approver DM target resolution, Matrix approval metadata
|
||||
- Encryption and Verification: Encryption setup, Encrypted media upload/download, Legacy state
|
||||
@@ -1,11 +0,0 @@
|
||||
# Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`mattermost-line-irc-nextcloud-talk-nostr-twitch-tlon-synology-chat` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Mattermost bot account setup, WebSocket inbound monitoring, Outbound delivery, LINE Messaging API webhook setup, Signed inbound webhook events, Rich LINE payloads, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text, IRC server/nick/TLS/NickServ setup, Raw IRC receive/send, Probe/status, Twitch bot account setup, Twitch IRC monitor/client lifecycle, Message tool send action, Nostr key setup, NIP-04 encrypted DM receive/send, Profile import/publish, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text and URL media delivery, Twitch bot account setup, Twitch IRC monitor/client lifecycle, Message tool send action, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion
|
||||
- Access and Identity: Mattermost bot account setup, WebSocket inbound monitoring, Outbound delivery, LINE Messaging API webhook setup, Signed inbound webhook events, Rich LINE payloads, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text, IRC server/nick/TLS/NickServ setup, Raw IRC receive/send, Probe/status, Twitch bot account setup, Twitch IRC monitor/client lifecycle, Message tool send action, Nostr key setup, NIP-04 encrypted DM receive/send, Profile import/publish, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text and URL media delivery, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion
|
||||
- Conversation Routing and Delivery: Mattermost bot account setup, WebSocket inbound monitoring, Outbound delivery, LINE Messaging API webhook setup, Signed inbound webhook events, Rich LINE payloads, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text, IRC server/nick/TLS/NickServ setup, Raw IRC receive/send, Probe/status, Twitch bot account setup, Twitch IRC monitor/client lifecycle, Message tool send action, Nostr key setup, NIP-04 encrypted DM receive/send, Profile import/publish, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text and URL media delivery, Twitch bot account setup, Twitch IRC monitor/client lifecycle, Message tool send action, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion
|
||||
- Media and Rich Content: LINE Messaging API webhook setup, Signed inbound webhook events, Rich LINE payloads, Nextcloud Talk bot installation, Webhook ingress, Outbound markdown/text, Synology Chat incoming/outgoing webhook setup, Webhook token verification, Outbound text, Nostr key setup, NIP-04 encrypted DM receive/send, Profile import/publish, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion, Tlon/Urbit ship URL/code setup, Urbit API auth/session, Rich text conversion
|
||||
@@ -1,13 +0,0 @@
|
||||
# Media understanding and media generation Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`media-understanding-and-media-generation` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Media Intake and Access: Local and remote media references, MIME and type detection, Size caps and bounded reads, Safe remote fetch, Local root policy, Inbound media store, PDF/document extraction dispatch, QR and media helper classification
|
||||
- Channel Media Handling: Inbound attachment staging, Sandbox media rewrites, Reply media templating, Message-tool attachment delivery, Duplicate delivery suppression
|
||||
- Media Configuration: Media capability configuration
|
||||
- Text-to-Speech Delivery: TTS, Outbound Voice Audio Delivery
|
||||
- Media Understanding: Audio attachment selection, Batch STT provider and CLI fallback, Voice-note mention preflight, Transcript insertion and echo, Audio proxy and limit handling, Inbound image summarization, Active vision model bypass, Text-only model media offload, Vision provider fallback, Image and PDF input routing, Video Understanding, Direct Video Analysis
|
||||
- Media Generation: Image generation tool invocation, Provider and model selection, Reference image editing, Generated image task lifecycle, Generated image persistence and delivery, Music generation tool invocation, Provider and model selection, Lyrics, instrumental, duration, and format controls, Reference inputs where supported, Music task lifecycle and duplicate status, Generated audio persistence and delivery, Video generation tool invocation, Mode and provider capability selection, Reference image, video, and audio inputs, Provider option validation, Video task lifecycle and status, Generated video persistence and delivery
|
||||
@@ -1,12 +0,0 @@
|
||||
# Microsoft Teams Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`microsoft-teams` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Teams CLI app creation, Bot registration and manifest upload, Credential configuration, Teams app install verification, Setup status, Probe and scope reporting, Teams app doctor, Webhook and health diagnostics, Operator repair paths, Text formatting and chunking, Adaptive and presentation cards, Progress streaming, Delivery receipts and errors, Queued and proactive replies, Webhook Runtime, SDK Lifecycle, Proactive Cloud Boundary, Setup status, Probe and scope reporting, Teams app doctor, Webhook and health diagnostics, Operator repair paths, Webhook Runtime, SDK Lifecycle, Proactive Cloud Boundary
|
||||
- Access and Identity: DM pairing, Stable sender identity, Allowlists and access groups, Invoke and command authorization, Teams-originated config writes, Bot Framework SSO invokes, Delegated token storage, Graph directory lookup, Member profile lookup, Bot Framework SSO invokes, Delegated token storage, Graph directory lookup, Member profile lookup
|
||||
- Conversation Routing and Delivery: Team and channel allowlists, Deterministic channel replies, Mention-gated group access, Session routing, Reply and thread context, Text formatting and chunking, Adaptive and presentation cards, Progress streaming, Delivery receipts and errors, Queued and proactive replies, Webhook Runtime, SDK Lifecycle, Proactive Cloud Boundary, Text formatting and chunking, Adaptive and presentation cards, Progress streaming, Delivery receipts and errors, Queued and proactive replies, Webhook Runtime, SDK Lifecycle, Proactive Cloud Boundary
|
||||
- Media and Rich Content: Inbound attachments, Graph-hosted media, File consent, SharePoint and OneDrive sharing, Media fetch safety
|
||||
- Native Controls and Approvals: Message action discovery, Polls and reactions, Read, edit, delete, and pin, Native approval cards, Feedback and group actions
|
||||
@@ -1,31 +0,0 @@
|
||||
# Multi-Agent Orchestration Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`multi-agent-orchestration` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can an operator configure and run the category workflow end to end?
|
||||
- Are the taxonomy features present as supported user paths rather than partial config fragments?
|
||||
- Are setup, normal operation, status or inspection, recovery, and removal paths represented where relevant?
|
||||
- Are channel, account, workspace, auth, task, and delegate variants covered where the category expects them?
|
||||
- Do known gaps leave major coordination or isolation branches missing?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness is the operator-facing system for setup, isolation, conversation routing, account routing, specialist lanes, delegate identity, status, recovery, and safe defaults.
|
||||
- A complete category lets multiple agents be created, isolated, routed, delegated, and inspected without implicit cross-agent leakage.
|
||||
- Undocumented config, nondeterministic routing, or unclear ownership of state, credentials, and outbound delivery are material completeness gaps.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Agent Setup: add agents, agent list/delete, identity files, non-interactive setup, and single-agent default.
|
||||
- Agent Isolation: workspace separation, state separation, auth separation, session separation, and tool profiles.
|
||||
- Conversation Routing: agent selection, route precedence, default fallback, peer overrides, and cross-channel examples.
|
||||
- Account Routing: multi-account setup, account selection, default accounts, account credentials, and delivery targets.
|
||||
- Specialist Lanes: lane contracts, background handoff, concurrency controls, priority controls, and coordinator handoff.
|
||||
- Delegate Identities: named delegates, authority model, delegate tiers, identity delegation, and organizational assistants.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Native Windows CLI and Gateway Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`native-windows-cli-and-gateway` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Setup: PowerShell installer, Node and package-manager bootstrap, npm global install, Packaged CLI launcher, Windows command shims, openclaw onboard, Local Gateway config, Daemon install flags, Native-vs-WSL setup boundary
|
||||
- Gateway Management: openclaw gateway, Foreground runtime health/readiness, Windows-specific restart/signal, Unmanaged foreground mode, openclaw gateway install, Gateway launcher files, Scheduled Task runtime status, Startup-folder fallback, openclaw status, Windows service inspection, Post-install diagnostics
|
||||
- Networking: Native Windows host binding, netsh interface portproxy, Gateway status and probe output, Loopback, LAN, and WSL boundary
|
||||
- Updates: openclaw update on native Windows package, Managed Gateway stop/restart, Detached update handoff, Windows package locks
|
||||
@@ -1,12 +0,0 @@
|
||||
# Native Windows companion app Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`native-windows-companion-app` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Installation and Updates: Official app download, MSI/MSIX/App Installer/winget-style packaging, Windows architecture handling for x64, App release channel
|
||||
- Gateway Connection: App-managed local Gateway attach/start, Remote Gateway connection modes, Device/node pairing
|
||||
- Chat Sessions: Native Windows chat window, Gateway chat transport
|
||||
- Status and Repair: App health states, App-specific repair, Windows system tray app, Status indicators, App-specific notification permission
|
||||
- Desktop Tools and Permissions: Windows node identity, Host command execution, Desktop command policy, App approval prompts, Screen and media capture, Canvas host behavior, Windows shell integrations, App secrets, Windows ACL, Command approval
|
||||
@@ -1,12 +0,0 @@
|
||||
# Nix install path Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`nix-install-path` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Install Handoff: Nix install overview, nix-openclaw source-of-truth, Install discoverability, Verification handoff
|
||||
- Plugin Lifecycle: Lifecycle command refusal, Declarative plugin selection, Nix-store plugin loading, Hardlink safety
|
||||
- Activation and App UX: Environment activation, macOS defaults activation, Runtime Nix-mode detection, Stable Nix defaults, Managed-by-Nix banner, Read-only config controls, Onboarding skip
|
||||
- Config and State: Immutable config guard, Config writer refusal, Agent-first Nix edits, Explicit config path, Writable state directory, Immutable-store config support, State integrity checks
|
||||
- Service Runtime and Guards: Nix profile PATH discovery, Profile precedence, Service PATH fallback, Trusted binary boundaries, Setup write refusal, Doctor repair refusal, Update handoff, Service lifecycle handoff
|
||||
@@ -1,12 +0,0 @@
|
||||
# OpenAI / Codex provider path Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`openai-codex-provider-path` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Model and Auth: Canonical OpenAI Model Routing, Catalog, Codex OAuth Profiles, Subscription Usage, Doctor Diagnostics, Operator Repair
|
||||
- Responses and Tool Compatibility: Codex Responses Transport, Payload Compatibility, Tool Context, Capability Compatibility
|
||||
- Native Codex Harness: Native Codex App-server Harness, Thread Lifecycle
|
||||
- Image and Multimodal Input: Image Generation Editing, Multimodal Input
|
||||
- Voice and Realtime Audio: Realtime Voice Transcription, Speech
|
||||
@@ -1,31 +0,0 @@
|
||||
# OpenClaw App SDK Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`openclaw-app-sdk` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can an external app developer complete the category workflow using public SDK APIs?
|
||||
- Are the taxonomy features represented by stable client contracts rather than protocol-only fragments?
|
||||
- Are setup, authentication, streaming, result handling, error behavior, and compatibility expectations documented?
|
||||
- Are browser, Node, React, testing, and custom transport variants covered where the category expects them?
|
||||
- Do known gaps leave major external-app capability branches missing?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness is the external app-developer workflow from connection through agent runs, sessions, events, approvals, resources, compatibility, and operational error handling.
|
||||
- A complete SDK category exposes typed, documented, reusable client APIs instead of requiring low-level Gateway protocol work.
|
||||
- Manual Gateway frame construction or reliance on internal package shapes is a material completeness gap.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Client API: SDK entrypoints, namespace layout, package split, and app/plugin boundary.
|
||||
- Gateway Access: Gateway connect, URL and token config, auto gateway, custom transport, and scopes/redaction.
|
||||
- Agent Conversations: agent handles, agent runs, run results, session creation, session send, and session controls.
|
||||
- Events and Approvals: event stream, event envelope, replay cursors, approval callbacks, and questions.
|
||||
- Resource Helpers: models, ToolSpace, artifacts, tasks, and environments.
|
||||
- Compatibility: generated client, ergonomic wrappers, unsupported calls, schema alignment, and public package contract.
|
||||
@@ -1,11 +0,0 @@
|
||||
# OpenRouter provider path Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`openrouter-provider-path` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Provider Setup and Auth: First-run setup, Default model selection, Provider plugin registration, Model-ref examples, OPENROUTER_API_KEY, Auth profiles and auth order, Status/probe and removal, Provider-entry SecretRef/API-key resolution, Gateway env inheritance, Static catalog rows, Dynamic /models discovery, openrouter/auto and nested refs, Free-model scan/probe, Model list/picker cache
|
||||
- Chat Runtime and Normalization: Chat completions route, Provider routing params, Per-model route overrides, Reasoning payload policy, Anthropic/Gemini/DeepSeek variants, Streamed content parsing, reasoning_details visible output, Tool-call delta preservation, Family-specific replay policy, Response-model and usage normalization, Attribution headers, Response-cache headers/TTL/clear, Anthropic cache-control markers, Cache usage mapping, Custom proxy exclusions
|
||||
- Provider Recovery and Diagnostics: Timeout/retry classification, Auth/billing/key-limit classification, Context overflow, Model fallback notices, Guarded fetch/pricing warnings
|
||||
- Media Generation and Speech: image_generate OpenRouter route, video_generate async jobs/polling/download, music_generate audio route, Text-to-speech, Speech-to-text transcription, Inbound media understanding, Generated artifact delivery
|
||||
@@ -1,40 +0,0 @@
|
||||
# Plugin Surface Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`plugin-sdk-and-bundled-plugin-architecture` surface.
|
||||
|
||||
## Surface-Specific Scoring Questions
|
||||
|
||||
For each category, ask:
|
||||
|
||||
- Can the intended plugin task be completed end to end by an author or
|
||||
operator?
|
||||
- Are the important plugin variants present for this category, such as channel,
|
||||
provider, tool, bundled, local, npm, or ClawHub flows?
|
||||
- Are the main lifecycle stages present where relevant: create, configure,
|
||||
validate, run, update, and remove or roll back?
|
||||
- Are compatibility, approval, or safety branches present when the category
|
||||
implies them?
|
||||
- Are important author/operator-visible gaps still forcing workarounds or
|
||||
unsupported paths?
|
||||
|
||||
## Surface-Specific Guidance
|
||||
|
||||
Variation from the default completeness process:
|
||||
|
||||
- Completeness is the plugin author or operator lifecycle for authoring, packaging, installing, running, approving, publishing, and testing plugins, not just SDK or runtime primitives.
|
||||
- Score the plugin surface against the full plugin journey, not only one import path, packaging mode, or runtime path.
|
||||
- Bundled-only support or support for only selected plugin families is incomplete when the category implies broader plugin capability.
|
||||
- Publishing and testing categories should include expected lifecycle support, not just raw commands or fixtures.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Authoring and Packaging plugins: Root SDK entrypoint, Focused SDK imports, Entrypoint discovery, Migration shims, Plugin manifest, Package metadata, Runtime compatibility, Validation feedback
|
||||
- Bundled plugins: Bundled plugin listing, Bundled source overlays, Packaged bundled plugins, Generated plugin inventory, Bundled channel IDs
|
||||
- Canvas plugin: Hosted Canvas and A2UI surfaces, Agent canvas tool, Node Canvas commands, Control UI embeds, Canvas documents, A2UI transport and snapshots
|
||||
- Installing and running plugins: Plugin setup, Runtime activation, Enable and disable, Safe load failures, Dependency repair, Install update and uninstall
|
||||
- Channel plugins: Inbound event handling, Outbound delivery, Ingress authorization, Destination resolution, Native approval prompts
|
||||
- Provider and tool plugins: Provider plugins, Tool plugins, Model catalogs, Provider auth, Web search and fetch, Mixed plugins
|
||||
- Plugin approvals: Approval requests, Native approval delivery, Same-chat fallbacks, Exec and plugin separation, Approval replay protection, Security helpers
|
||||
- Publishing plugins: Install sources, ClawHub publishing, npm publishing, Compatibility signaling, Update and rollback expectations, Third-party publication rules
|
||||
- Testing plugins: Test fixtures, Local test environment, Plugin runtime harness, Unit and integration scaffolds, Docker lifecycle suites, Smoke tests
|
||||
@@ -1,11 +0,0 @@
|
||||
# Raspberry Pi / small Linux devices Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`raspberry-pi-small-linux-devices` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Setup and Compatibility: Hardware and 64-bit OS requirements, Node runtime setup, OpenClaw install and onboarding, First-run verification, Supported Pi model selection, 64-bit ARM boundary, Unsupported device guidance, Slow-device caveats, npm/pnpm/Bun install modes, Installer architecture detection, Optional ARM binary checks, Fallback/build guidance
|
||||
- Remote Access and Auth: Headless API-key auth, Gateway shared-secret auth, Device pairing approvals, SecretRef handling, Token drift recovery, SSH tunnel dashboard access, Tailscale Serve/Funnel, Loopback/non-loopback exposure controls, Authenticated Control UI access
|
||||
- Gateway Runtime: Always-on Gateway process, Cloud model configuration, Channel startup, Gateway health/status, User service install, linger/boot persistence, Service drop-ins, Restart tuning, Status/log inspection, Backup/restore
|
||||
- Performance and Diagnostics: Swap and low-RAM tuning, USB SSD guidance, Compile cache/no-respawn settings, OOM/performance troubleshooting, Diagnostics bundles
|
||||
@@ -1,13 +0,0 @@
|
||||
# Security, auth, pairing, and secrets Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`security-auth-pairing-and-secrets` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Approval Policy and Tool Safeguards: Approval Policy, Dangerous Tool Safeguards
|
||||
- Gateway Auth and Remote Access: Shared Gateway token/password auth, Gateway auth mode, Trusted-proxy identity, Tailscale Serve/Funnel, Bind and origin restrictions, WebSocket handshake auth, Operator-facing docs, Browser Control UI, Remote Client Trust
|
||||
- Channel Access Control: Channel Identity, Allowlists, Sender Pairing
|
||||
- Device and Node Pairing: Setup codes, Device identity creation, Device-token issuance, Device pairing approvals for operator, Operator scopes that gate pairing, Local Control UI, Auth migration, Operator-facing docs, Node Pairing, Capability Trust, Remote Exec Approvals
|
||||
- Plugin Trust: Plugin Installation Trust, Security Boundaries
|
||||
- Credential and Secret Hygiene: Provider Auth Profiles, API Key Health, Secrets Storage, Redaction, Configuration Hygiene
|
||||
@@ -1,17 +0,0 @@
|
||||
# Session, memory, and context engine Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`session-memory-and-context-engine` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- CLI Session and Transcript Management: CLI Session, Transcript Management
|
||||
- Compaction, Pruning, and Token Pressure: Compaction, Pruning, Token Pressure
|
||||
- Context Engine and Runtime Assembly: Context Engine, Runtime Assembly
|
||||
- Cross-client History and Session Parity: Cross-client History, Session Parity
|
||||
- Diagnostics, Maintenance, and Recovery: Diagnostics, Maintenance, Recovery
|
||||
- Instruction Profile and Context Visibility: Instruction Profile, Context Visibility
|
||||
- Memory Backend Storage and Embedding Search: Memory Backend Storage, Embedding Search
|
||||
- Memory Files, Tools, and Active Memory: Memory Files, Tools, Active Memory
|
||||
- Session Routing and Conversation Binding: Session Routing, Conversation Binding
|
||||
- Transcript Persistence and Durability: Transcript Persistence, Durability
|
||||
@@ -1,12 +0,0 @@
|
||||
# Signal Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`signal` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Setup and Account Health: QR link setup, SMS registration, Installer and binary setup, Container account provisioning, Status probes, Setup diagnostics, Account safety guardrails
|
||||
- Conversation Access and Routing: DM pairing, DM allowlists, Sender identity normalization, Group allowlists, Mention gates, Pending group history
|
||||
- Message Delivery and Actions: Text delivery targets, Media delivery and limits, Typing and read receipts, Styled/chunked output, Reaction action discovery, Add/remove reactions, Group reaction targeting
|
||||
- Native Approvals: Native approval routing, Reaction approval responses, Approver targeting
|
||||
- Transport: Native daemon transport, Container transport, API mode selection, Receive reconnect/readiness
|
||||
@@ -1,12 +0,0 @@
|
||||
# Slack Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`slack` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: App Install, Slack app credentials, Manifest, Scopes, Channel status diagnostics, Slack account status, Operator Repair, Socket, HTTP transport, Runtime Lifecycle, Socket, HTTP transport, Runtime Lifecycle, Channel status diagnostics, Slack account status, Operator Repair
|
||||
- Access and Identity: Channel allowlists, Thread routing, Session Isolation, DM Pairing, Sender Authorization
|
||||
- Conversation Routing and Delivery: Channel allowlists, Thread routing, Session Isolation, DM Pairing, Sender Authorization, Outbound Delivery, Streaming, Reactions, Media, Attachments, Files, Vision, Outbound Delivery, Streaming, Reactions, Media, Attachments, Files, Vision
|
||||
- Media and Rich Content: Outbound Delivery, Streaming, Reactions, Media, Attachments, Files, Vision
|
||||
- Native Controls and Approvals: Slash Commands, Native Command Routing, Interactive Replies, App Home, Assistant Events, Native Approvals, Actions, Security-sensitive Ops, Interactive Replies, App Home, Assistant Events, Native Approvals, Actions, Security-sensitive Ops
|
||||
@@ -1,12 +0,0 @@
|
||||
# Telegram Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`telegram` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: BotFather token creation, TELEGRAM_BOT_TOKEN, Setup wizard credential capture, Startup getMe, Doctor/status surfacing, Named account configuration, CLI/message-tool targets, Directory adapters, Channel status, Account-scoped outbound, Long polling runner startup, Webhook listener startup, Reconnect, Restart, Named account configuration, Directory adapters and configured peers/groups for, Channel status, Account-scoped outbound, Long polling runner startup, Reconnect, Restart
|
||||
- Access and Identity: dmPolicy modes, Pairing-code approval, Numeric Telegram user ID normalization with telegram, allowFrom, Unauthorized DM, Group allowlists, Supergroup negative chat IDs, Forum topic session keys, ACP topic routing, Session key construction
|
||||
- Conversation Routing and Delivery: dmPolicy modes, Pairing-code approval, Numeric Telegram user ID normalization with telegram, allowFrom, Unauthorized DM, Group allowlists, Supergroup negative chat IDs, Forum topic session keys, ACP topic routing, Session key construction, Inbound media download, Voice notes, Location, Poll sending, Reactions, Text, Preview streaming, Reply threading tags, Durable outbound message recording, Voice notes, Poll sending, Reply threading tags, Durable outbound message recording
|
||||
- Media and Rich Content: Inbound media download, Voice notes, Location, Poll sending, Reactions, Text, Preview streaming, Reply threading tags, Durable outbound message recording, Voice notes, Poll sending, Reply threading tags, Durable outbound message recording, Inbound media download, Voice notes, Location and venue extraction into channel context, Poll sending, Reactions
|
||||
- Native Controls and Approvals: Inline keyboard rendering, Exec approvals in DMs, Message actions, Action capability discovery, Native setMyCommands startup sync, Command name/description normalization, Built-in commands, Command authorization in DMs, Model buttons, Native `setMyCommands` startup sync, Command name/description normalization, Built-in commands such as `/help`, Command authorization in DMs, Model buttons and command UI helpers
|
||||
@@ -1,12 +0,0 @@
|
||||
# Observability Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`telemetry-diagnostics-and-observability` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Health and Repair: Background health-monitor loop, Per-account enable/disable settings, Startup grace, Restart logging, openclaw doctor, Structured health checks, Core doctor checks, Plugin SDK doctor/health contracts, openclaw status, openclaw health, Gateway RPC health, Cached health snapshots
|
||||
- Logging: Rolling Gateway JSONL file logs, openclaw logs, Gateway RPC logs.tail, Redaction patterns and sinks, Trace correlation fields
|
||||
- Diagnostic Collection: openclaw gateway diagnostics export, openclaw gateway stability --bundle, Chat /diagnostics, Support zip composition, Bounded in-process stability recorder, openclaw gateway stability, Memory pressure events, Critical memory pressure snapshot option
|
||||
- Telemetry Export: Diagnostic event types, Async dispatch, W3C trace context creation, Plugin SDK diagnostic runtime exports, Model-call diagnostic events, diagnostics-otel plugin install, OTLP/HTTP traces, Trusted trace context, Model and runtime telemetry, diagnostics-prometheus plugin install, Gateway-authenticated GET /api/diagnostics/prometheus, Prometheus text exposition, Trusted diagnostic event subscription
|
||||
- Session Diagnostics: session.state, Diagnostic session activity snapshots, Model usage, Export of session signals to stability
|
||||
@@ -1,12 +0,0 @@
|
||||
# TUI Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`tui-and-terminal-ux` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Runtime Modes: Gateway TUI launch, Local chat launch, Terminal alias launch, Initial message launch, Launch option validation, Gateway connection, Gateway authentication, History load on attach, Reconnect visibility, Gateway command RPCs, Embedded local chat, Local auth flow, Config repair loop, Gateway-free recovery
|
||||
- Input and Commands: Message composition, Input history, Keyboard shortcuts, Paste and busy-submit handling, IME and AltGr handling, Slash Commands, Pickers, Settings
|
||||
- Session Management: Session Lifecycle, History, Resume
|
||||
- Local Shell Execution: Bang-command routing, Approval prompt, Command output display, Execution environment marker
|
||||
- Rendering and Output Safety: Streaming Message Rendering, Tool Cards, Terminal Rendering Primitives, Output Safety
|
||||
@@ -1,13 +0,0 @@
|
||||
# Voice and realtime talk Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`voice-and-realtime-talk` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Talk Providers: OpenAI Realtime voice backend bridge, Google Gemini Live backend bridge, Realtime voice provider SDK contracts, Provider diagnostics, Talk catalog, Talk provider config, Shared native config parsing
|
||||
- Realtime Talk Sessions: Agent consult handoff, Active Talk agent-run status, Talkback runtime behavior, Forced consult scheduling, Browser Talk start/stop UI, Browser WebRTC sessions, Browser relay mode, Browser tool-call forwarding, Realtime session controls, Gateway relay sessions, Audio-frame limits
|
||||
- Speech and Transcription: Voice directives, Talk speech playback, Transcription relay sessions, Realtime transcription providers, Native directive parsing
|
||||
- Native App Talk: macOS native Talk mode, iOS Talk mode, Android Talk mode, Shared Talk config
|
||||
- Voice Wake and Routing: Wake-word settings, Wake routing, macOS Voice Wake runtime, Mobile wake preferences
|
||||
- Talk Observability: Talk event logging, Session-log health, Live smoke output, Prometheus diagnostic counters, Operator visibility into setup
|
||||
@@ -1,12 +0,0 @@
|
||||
# Voice Call channel Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`voice-call-channel` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Voice Call Channel, Voice Call Channel, Voice Call Channel
|
||||
- Access and Identity: Voice Call Channel
|
||||
- Conversation Routing and Delivery: Voice Call Channel
|
||||
- Media and Rich Content: Voice Call Channel, Voice Call Channel
|
||||
- Realtime Voice and Calls: Voice Call Channel, Voice Call Channel, Voice Call Channel, Voice Call Channel, Voice Call Channel
|
||||
@@ -1,12 +0,0 @@
|
||||
# watchOS companion surfaces Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`watchos-companion-surfaces` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Delivery and Recovery: APNs relay/direct registration as it affects, Silent push, Pending approval recovery IDs, Gateway-side iOS exec approval, iPhone-side WatchConnectivity transport, Watch-side receiver activation, Delivery fallback among reachable messages
|
||||
- Exec Approvals: Watch exec approval prompt, Watch approval list/detail UI, iPhone-side prompt caching
|
||||
- Distribution and Support: Watch app, Signing/profile variables, Public/support status, Changelog, Release metadata, Historical bug/regression themes relevant to scoring
|
||||
- Notifications and Replies: watch.status, Payload normalization, Mirrored iOS notification fallback when watch, Watch action buttons from generic prompt, Watch-to-iPhone reply payloads, iPhone-side dedupe, Mirrored iOS notification action
|
||||
- Watch App UI: Watch app entry point, Generic inbox, Persistent watch inbox state
|
||||
@@ -1,11 +0,0 @@
|
||||
# Web search tools Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`web-search-tools` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Search Providers: API-backed providers, Keyless and self-hosted providers, Provider comparison and auto-detection, Provider-specific filters and extraction, Result normalization, OpenAI native web_search, Codex native web_search, Gemini grounding, Grok web grounding, Kimi web search, Provider-native citations, Model and filter routing, webSearchProviders, registerWebSearchProvider, webFetchProviders, registerWebFetchProvider, public-artifact loading, runtime resolution, contract tests
|
||||
- Setup and Diagnostics: Provider credentials, Default provider selection, Credential repair, Status checks, Quota errors, Cache controls, Provider diagnostics, Retry and fallback, Operator repair
|
||||
- Network Safety: Network Safety, SSRF, Redirects, Untrusted Content
|
||||
- Tool Availability and Fetch: web_search exposure, web_fetch exposure, x_search exposure, group:web policy, disabled-state diagnostics, provider/model gating, URL fetch, HTML extraction, PDF/text extraction, Safe truncation, Content citation handoff
|
||||
@@ -1,12 +0,0 @@
|
||||
# WhatsApp Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`whatsapp` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- Channel Setup and Operations: Official @openclaw/whatsapp plugin metadata, openclaw plugin install whatsapp, Channel config schema, Baileys socket lifecycle, Operator troubleshooting, Baileys socket lifecycle, Operator troubleshooting for reconnect loops
|
||||
- Access and Identity: QR login, Baileys multi-file auth persistence, DM pairing challenge, Multi-account/default-account resolution, Direct-message dmPolicy, Sender identity extraction, Privacy controls for plugin hooks, Direct-message `dmPolicy`, Sender identity extraction, Privacy controls for plugin hooks and
|
||||
- Conversation Routing and Delivery: Group allowlists, Group session keys, Outbound text sends, Provider-accepted receipts, Outbound text sends, Provider-accepted receipts and durable delivery identifiers
|
||||
- Media and Rich Content: Inbound media download, Outbound image
|
||||
- Native Controls and Approvals: Native exec, Approver target resolution
|
||||
@@ -1,12 +0,0 @@
|
||||
# Windows via WSL2 Completeness
|
||||
|
||||
Use this rubric when assigning category Completeness scores for the
|
||||
`windows-via-wsl2` surface.
|
||||
|
||||
## Category Scope
|
||||
|
||||
- WSL Setup and Updates: WSL2 + Ubuntu installation, Node runtime, Linux install flow inside WSL2, WSL2 runtime boundary, WSL2 network-family requirements, Source install and build inside WSL2, openclaw update, npm/pnpm/git package-root, Managed systemd Gateway restart, Service metadata refresh, Package-manager caveats
|
||||
- Gateway Service Lifecycle: Onboarded systemd install, Gateway service install, systemd user unit rendering, WSL-aware systemd unavailable hints, Doctor service repair, WSL user-service linger, Systemd availability after Windows boot, Windows startup task for WSL, Verification before Windows sign-in, Clear expectations around PC power
|
||||
- Gateway Access and Exposure: Gateway token/password auth, Provider credentials, Gateway auth SecretRefs, Remote URL credential precedence, WSL virtual network, Windows portproxy setup, Windows Firewall rules, Reachable Gateway URLs, Loopback and LAN exposure, WSL2 IPv4 networking, Tailscale remote access
|
||||
- Diagnostics and Repair: openclaw doctor, openclaw status, openclaw logs, SecretRef, WSL/systemd unavailable hints, Operator repair guidance after WSL2 service
|
||||
- Browser and Control UI: WSL2 Gateway with Windows browser, Windows Control UI URL, Raw remote CDP to Windows Chrome, Host-local Chrome MCP, Browser profile cdpUrl, Layered diagnostics
|
||||
@@ -98,7 +98,7 @@ Do not close from title alone. If closing as done on main or nonsensical, prove
|
||||
|
||||
When asked for `5 new`, exclude refs already surfaced in the session and refill from the archive until there are 5 live-open candidates. If fewer than 5 remain open, list all open ones and say how many short.
|
||||
|
||||
When asked to `update`, `refresh`, `recheck`, `check again`, or similar, return an updated live-open candidate list. Sort by maintainer importance, not recency: high-impact ready fixes first, then useful-but-review-first, then open/not-ready items. Do not include a "changed since last pass" section or bottom-line merged/closed summary unless the user explicitly asks for churn.
|
||||
When asked to `update`, `refresh`, `recheck`, `check again`, or similar, return an updated live-open candidate list. Do not fill the main list with items that merely merged/closed since the last pass; put those numbers in a short bottom line.
|
||||
|
||||
Prefer:
|
||||
|
||||
@@ -142,20 +142,18 @@ No Markdown tables. Compact bullets. Use color/risk markers:
|
||||
Required line shape:
|
||||
|
||||
```markdown
|
||||
- **PR #81244** `@whatsskill.` `+118/-1` `bug` 🟢 https://github.com/openclaw/openclaw/pull/81244 - Prevents chat action buttons from overlapping short assistant replies. Verifiable: yes. Blast: web chat rendering, low.
|
||||
- **Issue #81245** `@alice` `LOC n/a` `bug` 🟡 https://github.com/openclaw/openclaw/issues/81245 - Reports duplicate Telegram replies when reconnecting after gateway restart. Verifiable: partial. Blast: Telegram channel runtime, medium.
|
||||
- **PR #81244** `@whatsskill.` `+118/-1` `bug` 🟢 verifiable: yes. This prevents chat action buttons from overlapping short assistant replies. Blast: web chat rendering, low.
|
||||
- **Issue #81245** `@alice` `LOC n/a` `bug` 🟡 verifiable: partial. This reports duplicate Telegram replies when reconnecting after gateway restart. Blast: Telegram channel runtime, medium.
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Bold the `PR #n` or `Issue #n` marker.
|
||||
- Use `@handle`, not author bio text.
|
||||
- Always include the full GitHub URL.
|
||||
- Include a one-line description after the URL, separated with `-`.
|
||||
- PR LOC is `+additions/-deletions`; issue LOC is `LOC n/a`.
|
||||
- Type: `bug`, `feature`, `perf`, `security`, `docs`, `test`, `chore`, or `refactor`.
|
||||
- Write a full sentence for what it does.
|
||||
- Always include blast radius in one phrase.
|
||||
- Always include `verifiable: yes|partial|no` plus the shortest proof hint when helpful.
|
||||
- If status is not open, still show it only when the user asked for all surfaced refs; use ✅ or ⚪ and state merged/closed.
|
||||
- For refresh-style asks, prefer section order: `Best Open Now`, `Useful But Review First`, `Still Open / Not Ready`. Omit merged/closed churn by default.
|
||||
- For refresh-style asks, bottom line: `Merged/closed since last pass: #81016 merged, #81026 closed.` Omit if none.
|
||||
|
||||
@@ -44,9 +44,7 @@ pnpm crabbox:run -- --help | sed -n '1,120p'
|
||||
- OpenClaw scripts prefer `../crabbox/bin/crabbox` when present. The user PATH
|
||||
shim can be stale.
|
||||
- Check `.crabbox.yaml` for direct-provider defaults. Omitting `--provider`
|
||||
means brokered AWS for normal Linux/macOS paths; the wrapper selects Azure
|
||||
for unqualified Windows/WSL2 runs when the local Crabbox binary advertises
|
||||
Azure.
|
||||
means brokered AWS today.
|
||||
- The brokered AWS default is a Linux developer image in `eu-west-1`; the repo
|
||||
config pins hot `eu-west-1a/b/c` placement so Fast Snapshot Restore can apply.
|
||||
If warmup drifts well past the minute-scale path, verify image promotion,
|
||||
@@ -54,13 +52,6 @@ pnpm crabbox:run -- --help | sed -n '1,120p'
|
||||
- For broad OpenClaw maintainer `pnpm` gates, prefer the repo wrapper with
|
||||
`--provider blacksmith-testbox` or the repo Testbox helpers when the standing
|
||||
Testbox policy applies.
|
||||
- Cold Testbox acquisition and hydration often take tens of seconds. When broad
|
||||
remote proof is likely, immediately start
|
||||
`node scripts/crabbox-wrapper.mjs warmup --provider blacksmith-testbox --keep --timing-json`
|
||||
in a background command session while inspecting, editing, and running
|
||||
focused local tests. Poll later, reuse the returned `tbx_...` with
|
||||
`--provider blacksmith-testbox --id <tbx_id>`, and stop it before handoff.
|
||||
Do not warm speculatively when remote proof is unlikely.
|
||||
- Always report the actual provider and id. `cbx_...` means AWS Crabbox;
|
||||
`tbx_...` means Blacksmith Testbox through Crabbox. If the output only says
|
||||
`blacksmith testbox list`, use `blacksmith testbox list --all` before
|
||||
@@ -91,16 +82,18 @@ Use these only when the task needs an existing non-Linux host. OpenClaw broad
|
||||
Linux validation uses the repo Crabbox config unless a provider is explicitly
|
||||
requested.
|
||||
|
||||
Native brokered Windows is available for Windows-specific proof. Prefer Azure
|
||||
for Windows/WSL2 when the subscription has quota or credits and the local
|
||||
Crabbox binary advertises Azure. Keep broad Linux gates on Linux/Testbox unless
|
||||
the bug is Windows-specific, and only force AWS when the operator asks for the
|
||||
older AWS developer image/cache path or Azure is unavailable:
|
||||
Native brokered Windows is available for Windows-specific proof. Use the AWS
|
||||
developer image in `us-west-2` on demand; it has the expected OpenClaw developer
|
||||
toolchain and Docker image cache. Keep broad Linux gates on Linux/Testbox unless
|
||||
the bug is Windows-specific:
|
||||
|
||||
```sh
|
||||
pnpm crabbox:warmup -- \
|
||||
../crabbox/bin/crabbox warmup \
|
||||
--provider aws \
|
||||
--target windows \
|
||||
--windows-mode wsl2 \
|
||||
--windows-mode normal \
|
||||
--region us-west-2 \
|
||||
--market on-demand \
|
||||
--timing-json
|
||||
```
|
||||
|
||||
@@ -167,14 +160,9 @@ pnpm crabbox:run -- \
|
||||
--ttl 240m \
|
||||
--timing-json \
|
||||
--shell -- \
|
||||
"pnpm verify"
|
||||
"pnpm test"
|
||||
```
|
||||
|
||||
Use `pnpm verify` when you need check plus full Vitest proof. It emits
|
||||
`CRABBOX_PHASE:check` and `CRABBOX_PHASE:test`, making Crabbox summaries show
|
||||
which stage failed. Use plain `pnpm test` only when check proof is already
|
||||
covered or intentionally skipped.
|
||||
|
||||
Focused rerun:
|
||||
|
||||
```sh
|
||||
@@ -230,21 +218,6 @@ Read the JSON summary and the Testbox line. Useful fields:
|
||||
- Actions run URL/id from the Testbox output
|
||||
- `exitCode`
|
||||
|
||||
Use provider-backed cache volumes only for rebuildable caches, not secrets or
|
||||
checkout state. On Blacksmith, Crabbox forwards them as sticky disks:
|
||||
|
||||
```sh
|
||||
node scripts/crabbox-wrapper.mjs run \
|
||||
--provider blacksmith-testbox \
|
||||
--cache-volume pnpm-store=openclaw-node24-pnpm-lock:/tmp/openclaw-pnpm-store \
|
||||
--timing-json \
|
||||
-- \
|
||||
corepack pnpm check:changed
|
||||
```
|
||||
|
||||
The selected provider must advertise cache-volume support. If not, omit
|
||||
`--cache-volume` and rely on kept-lease caches.
|
||||
|
||||
`blacksmith testbox list` may hide hydrating or ready boxes. Use:
|
||||
|
||||
```sh
|
||||
@@ -612,8 +585,7 @@ Crabbox Blacksmith backend delegates setup to:
|
||||
|
||||
The hydration workflow owns checkout, Node/pnpm setup, dependency install,
|
||||
secrets, ready marker, and keepalive. Crabbox owns dispatch, sync, SSH command
|
||||
execution, timing, logs/results, cleanup, and cache-volume requests. Blacksmith
|
||||
implements cache volumes as sticky disks.
|
||||
execution, timing, logs/results, and cleanup.
|
||||
|
||||
Minimal Blacksmith-backed Crabbox run, from repo root:
|
||||
|
||||
@@ -708,7 +680,6 @@ crabbox events <run_id> --json
|
||||
crabbox logs <run_id>
|
||||
crabbox results <run_id>
|
||||
crabbox cache stats --id <id-or-slug>
|
||||
crabbox cache volumes
|
||||
crabbox ssh --id <id-or-slug>
|
||||
blacksmith testbox list
|
||||
```
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
name: discord-user-post
|
||||
description: Post an approved message as the logged-in Discord user through the Discord desktop app. Use for release announcements or other direct user-authored Discord posts; not for OpenClaw channel sends, bots, webhooks, relays, agent sessions, or archive search.
|
||||
---
|
||||
|
||||
# Discord User Post
|
||||
|
||||
Use `$computer-use` to operate `/Applications/Discord.app` in the user's
|
||||
existing logged-in session. This workflow represents the user directly.
|
||||
|
||||
## Prepare
|
||||
|
||||
1. Draft the complete final message outside Discord.
|
||||
2. Confirm the intended server and channel with the user when either is
|
||||
ambiguous.
|
||||
3. Open Discord and navigate to the exact destination without entering the
|
||||
message.
|
||||
4. Verify the visible server name, channel header, and logged-in account.
|
||||
|
||||
Do not infer the target from unrelated Discord content. Stop if Discord is not
|
||||
logged in, the account is wrong, or the exact destination cannot be verified.
|
||||
|
||||
## Confirm and Post
|
||||
|
||||
Posting is representational communication. Follow the `$computer-use`
|
||||
confirmation policy even when the user previously asked for an announcement:
|
||||
|
||||
1. Show the user the exact final body and verified destination.
|
||||
2. Request action-time confirmation before typing into Discord.
|
||||
3. After confirmation, enter the approved body unchanged.
|
||||
4. Visually inspect the composed message and destination again.
|
||||
5. Send once.
|
||||
|
||||
If the body or destination changes after confirmation, request confirmation
|
||||
again before sending.
|
||||
|
||||
## Verify
|
||||
|
||||
- Confirm the message appears once, from the user's account, in the intended
|
||||
channel.
|
||||
- Report the server, channel, and visible send result.
|
||||
- Do not edit, delete, react, or send a follow-up without the corresponding
|
||||
user instruction and confirmation.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Never use `openclaw message`, an OpenClaw agent, a Discord bot, webhook, relay,
|
||||
or token for this workflow.
|
||||
- Never expose private Discord content or account details in public output.
|
||||
- Never send a draft, partial message, duplicate, or unreviewed attachment.
|
||||
- For Discord archive/history/search, use `$discrawl` instead.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "Discord User Post"
|
||||
short_description: "Post approved messages through the logged-in Discord app"
|
||||
default_prompt: "Post this approved message as me through the logged-in Discord desktop app."
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: discrawl
|
||||
description: "Discord archive: search, sync freshness, DMs, summaries, TUI, repo/release work."
|
||||
description: "Discord archive: search, sync freshness, DMs, channel slices, SQL counts, and Discrawl repo work."
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/openclaw/discrawl
|
||||
@@ -16,154 +16,29 @@ metadata:
|
||||
|
||||
# Discrawl
|
||||
|
||||
Use local Discord archive data first for Discord questions. Hit Discord APIs
|
||||
only when the archive is stale, missing the requested scope, or the user asks
|
||||
for current external context.
|
||||
|
||||
## Sources
|
||||
|
||||
- DB: platform-native XDG data dir, usually
|
||||
`${XDG_DATA_HOME:-~/.local/share}/discrawl/discrawl.db` on Linux or
|
||||
`~/Library/Application Support/discrawl/discrawl.db` on macOS
|
||||
- Config: platform-native XDG config dir, with legacy fallback to
|
||||
`~/.discrawl/config.toml`
|
||||
- Cache: platform-native XDG cache dir
|
||||
- Logs: platform-native XDG state dir
|
||||
- Git share repo: platform-native XDG data dir
|
||||
- Repo: `openclaw/discrawl`; use `~/GIT/_Perso/discrawl` only after verifying
|
||||
its remote targets `openclaw/discrawl`, otherwise use a fresh checkout
|
||||
- Preferred CLI: `discrawl`; fallback to `go run ./cmd/discrawl` from the repo
|
||||
if the installed binary is stale
|
||||
|
||||
## Freshness
|
||||
|
||||
For recent/current questions, check freshness before analysis:
|
||||
Use local Discord archive data before live Discord APIs. Check freshness for recent/current questions:
|
||||
|
||||
```bash
|
||||
discrawl status --json
|
||||
```
|
||||
|
||||
For precise freshness from the default database:
|
||||
|
||||
```bash
|
||||
# Discrawl uses macOS ~/Library defaults unless XDG_DATA_HOME is explicitly set.
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
db="$HOME/Library/Application Support/discrawl/discrawl.db"
|
||||
;;
|
||||
*)
|
||||
db="${XDG_DATA_HOME:-$HOME/.local/share}/discrawl/discrawl.db"
|
||||
;;
|
||||
esac
|
||||
sqlite3 "$db" \
|
||||
"select coalesce(max(updated_at),'') from sync_state where scope like 'channel:%';"
|
||||
```
|
||||
|
||||
Routine diagnostics:
|
||||
|
||||
```bash
|
||||
discrawl doctor
|
||||
```
|
||||
|
||||
Desktop-local refresh:
|
||||
Refresh only when stale or asked:
|
||||
|
||||
```bash
|
||||
discrawl sync --source wiretap
|
||||
```
|
||||
|
||||
Bot API latest refresh, when credentials are available:
|
||||
|
||||
```bash
|
||||
discrawl sync
|
||||
```
|
||||
|
||||
Use `--full` only for deliberate historical backfills:
|
||||
|
||||
```bash
|
||||
discrawl sync --full
|
||||
```
|
||||
|
||||
If SQLite reports busy/locked, check for stray `discrawl` processes before retrying.
|
||||
|
||||
## Query Workflow
|
||||
|
||||
1. Resolve scope: guild, channel, DM, author, keyword, date range.
|
||||
2. Check freshness for recent/current requests.
|
||||
3. Prefer CLI search/messages for slices; use read-only SQL for exact counts.
|
||||
4. Report absolute date spans, counts, channel/DM names, and known gaps.
|
||||
|
||||
Use root or subcommand help for syntax: `discrawl --help`,
|
||||
`discrawl help search`, `discrawl search --help`. Use
|
||||
`DISCRAWL_NO_AUTO_UPDATE=1` for read smokes when you do not want git-share
|
||||
updates.
|
||||
|
||||
Common commands:
|
||||
Query with bounded slices:
|
||||
|
||||
```bash
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl search --limit 20 "query"
|
||||
discrawl messages --channel '#maintainers' --days 7 --all
|
||||
discrawl dms --last 20
|
||||
discrawl tui --dm
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) from messages;"
|
||||
```
|
||||
|
||||
## SQL
|
||||
Report absolute date spans, channel/DM names, counts, and known gaps. Use read-only SQL for exact counts/rankings. Never use `--unsafe --confirm` unless the user explicitly requests a reviewed DB mutation.
|
||||
|
||||
Use `discrawl sql` for exact counts, joins, and ranking queries when normal
|
||||
CLI reads are too coarse. The command is read-only by default, accepts SQL as
|
||||
args or stdin, and supports `--json` for agent parsing.
|
||||
|
||||
Useful examples:
|
||||
|
||||
```bash
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) as messages from messages;"
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(c.name, ''), m.channel_id) as channel, count(*) as messages from messages m left join channels c on c.id = m.channel_id group by m.channel_id order by messages desc limit 20;"
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(mm.display_name, ''), nullif(mm.global_name, ''), nullif(mm.username, ''), m.author_id) as author, count(*) as messages from messages m left join members mm on mm.guild_id = m.guild_id and mm.user_id = m.author_id group by m.guild_id, m.author_id order by messages desc limit 20;"
|
||||
```
|
||||
|
||||
Never use `--unsafe --confirm` unless the user explicitly asks for a database
|
||||
mutation and the write has been reviewed.
|
||||
|
||||
When the installed CLI lacks a new feature, build or run from a verified
|
||||
`openclaw/discrawl` checkout before concluding the feature is missing.
|
||||
|
||||
## Discord Boundaries
|
||||
|
||||
Bot API sync requires configured Discord bot credentials; do not invent token
|
||||
availability. Desktop wiretap mode reads local Discord Desktop artifacts and
|
||||
must not extract credentials, use user tokens, call Discord as the user, or
|
||||
write to Discord application storage. Wiretap/Desktop cache DMs are local-only
|
||||
and must not be described as part of the published Git snapshot. Git-share
|
||||
snapshots must not include secrets or `@me` DM rows.
|
||||
|
||||
## Verification
|
||||
|
||||
For repo edits, prefer existing Go gates:
|
||||
|
||||
```bash
|
||||
GOWORK=off go test ./...
|
||||
```
|
||||
|
||||
Then run targeted CLI smoke for the touched surface, for example:
|
||||
|
||||
```bash
|
||||
discrawl doctor
|
||||
discrawl status --json
|
||||
DISCRAWL_NO_AUTO_UPDATE=1 discrawl search --limit 5 "test"
|
||||
```
|
||||
|
||||
## ClawSweeper Sandbox
|
||||
|
||||
Use the sandbox reader only:
|
||||
|
||||
```bash
|
||||
discrawl-sandbox search --limit 20 "query"
|
||||
discrawl-sandbox messages --channel clawtributors --days 7 --all
|
||||
discrawl-sandbox status --json
|
||||
```
|
||||
|
||||
This reader imports `https://github.com/openclaw/discord-store.git` into
|
||||
`/root/clawsweeper-sandbox-workspace/.discrawl/discrawl.db` with
|
||||
`discord.token_source = "none"`. The published Git snapshot is public-channel
|
||||
filtered; do not use `/root/.discrawl/config.toml` or the rich writer DB from
|
||||
sandboxed public Discord sessions.
|
||||
Boundaries: bot sync needs configured Discord bot credentials. Wiretap reads local Discord Desktop artifacts only; do not extract user tokens, call Discord as the user, or write to Discord storage. Git-share snapshots must not include secrets or `@me` DM rows.
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
---
|
||||
name: openclaw-changelog-update
|
||||
description: Regenerate OpenClaw release changelog sections from git history before beta or stable releases.
|
||||
---
|
||||
|
||||
# OpenClaw Changelog Update
|
||||
|
||||
Use this for release changelog rewrites and GitHub release-note source text.
|
||||
This is mandatory before every beta, beta rerun, stable release, or stable
|
||||
rerun. Use it with `release-openclaw-maintainer`; this skill owns changelog
|
||||
content, ordering, grouping, and attribution discipline.
|
||||
|
||||
## Goal
|
||||
|
||||
Rewrite the target `CHANGELOG.md` version section from history, not from stale
|
||||
draft notes. Produce grouped user-facing release notes sorted by user interest
|
||||
while preserving every relevant issue/PR ref and every human `Thanks @...`
|
||||
attribution.
|
||||
|
||||
## Inputs
|
||||
|
||||
- Target base version: `YYYY.M.PATCH`, without beta suffix.
|
||||
- Base tag: last reachable shipped release tag, usually the previous stable or
|
||||
the previous beta train requested by the operator.
|
||||
- Target ref: exact branch/SHA being released.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Start on `main` before branching when possible:
|
||||
- `git fetch --tags origin`
|
||||
- `git pull --ff-only`
|
||||
- confirm clean `git status -sb`
|
||||
2. Audit history, including direct commits:
|
||||
- `git log --first-parent --date=iso-strict --pretty=format:'%h%x09%ad%x09%s' <base-tag>..<target-ref>`
|
||||
- `git log --first-parent --grep='(#' --date=short --pretty=format:'%h%x09%ad%x09%s' <base-tag>..<target-ref>`
|
||||
- also inspect `--since='24 hours ago'` when main moved during the release.
|
||||
3. Read linked PRs/issues or diffs for ambiguous commits. Direct commits matter;
|
||||
infer notes from subject, body, touched files, tests, and nearby commits.
|
||||
4. Rewrite one stable-base section only:
|
||||
- use `## YYYY.M.PATCH`
|
||||
- do not create beta-specific headings
|
||||
- do not leave a stale `## Unreleased` section above the target release
|
||||
- if `Unreleased` contains release-bound notes, fold them into the target
|
||||
section instead of deleting them
|
||||
5. Section shape:
|
||||
- `### Highlights`: 5-8 bullets, broad user wins first
|
||||
- `### Changes`: new capabilities and behavior changes
|
||||
- `### Fixes`: user-facing fixes first, grouped by impact and surface
|
||||
- group related changes/fixes by surface and user impact; avoid one bullet
|
||||
per tiny commit when several commits tell one user-facing story
|
||||
6. Preserve attribution:
|
||||
- keep `#issue`, `(#PR)`, `Fixes #...`, and `Thanks @...`
|
||||
- every human-authored merged PR represented by a user-facing entry needs
|
||||
its PR ref and `Thanks @author`, even when the PR had no linked issue
|
||||
- every human issue reporter for a `Fixes #...` or referenced bug issue
|
||||
represented by a user-facing entry needs `Thanks @reporter` unless the
|
||||
same handle is already thanked in that bullet
|
||||
- every human `Co-authored-by` contributor on represented user-facing work
|
||||
needs `Thanks @handle` when a GitHub handle is known
|
||||
- when grouping multiple PRs/issues in one bullet, include every relevant
|
||||
PR/issue ref and every human contributor handle in that same bullet
|
||||
- multiple `Thanks @...` handles in one bullet are expected; do not drop or
|
||||
collapse contributor credit just because the note is grouped
|
||||
- if one grouped bullet covers both direct commits and PRs, keep all PR refs
|
||||
and thanks, plus any issue refs from the direct commits
|
||||
- before finalizing, audit the final release-note body:
|
||||
- extract all `#NNN` refs from the notes
|
||||
- resolve which refs are PRs and collect human PR authors
|
||||
- resolve issue refs used as bug/report refs and collect human reporters
|
||||
- scan represented commits for `Co-authored-by`
|
||||
- compare those handles to the final `Thanks @...` set
|
||||
- fix every missing human credit or explicitly record why it is omitted
|
||||
- do not add GHSA references, advisory IDs, or security advisory slugs to
|
||||
changelog entries or GitHub release-note text unless explicitly requested
|
||||
- never thank bots, `@openclaw`, `@clawsweeper`, or `@steipete`
|
||||
- do not use GitHub's release contributor count as the source of truth; the
|
||||
changelog must carry the complete human credit set itself
|
||||
7. Sorting preference:
|
||||
- security/data-loss and content-boundary fixes
|
||||
- transcript/replay/reply delivery correctness
|
||||
- channels and mobile integrations
|
||||
- providers/Codex/local model reliability
|
||||
- install/update/release path reliability
|
||||
- performance and observability
|
||||
- docs and contributor-only/internal details last or omitted
|
||||
8. Keep bullets single-line unless existing file style forces otherwise. Avoid
|
||||
internal release-process noise unless it changes user install/update safety.
|
||||
9. Check release-note side conditions:
|
||||
- inspect `src/plugins/compat/registry.ts`
|
||||
- inspect `src/commands/doctor/shared/deprecation-compat.ts`
|
||||
- if any compatibility `removeAfter` is on/before release date, resolve it
|
||||
or explicitly record the blocker before shipping
|
||||
10. Validate and ship:
|
||||
- generate and verify the complete contribution ledger before committing:
|
||||
```bash
|
||||
node .agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs \
|
||||
--base <base-tag> \
|
||||
--target <target-ref> \
|
||||
--version <YYYY.M.PATCH> \
|
||||
--write-ledger
|
||||
```
|
||||
- the command fails when any `#NNN` reference in release history or the
|
||||
rendered release section is absent from the ledger, when reverted work is
|
||||
presented as shipped, or when an eligible PR author, issue reporter, or
|
||||
known co-author is missing from that entry's `Thanks @...` credit
|
||||
- after the GitHub release or prerelease is published, verify every matching
|
||||
release page against the same source section:
|
||||
```bash
|
||||
node .agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs \
|
||||
--base <base-tag> \
|
||||
--target <target-ref> \
|
||||
--version <YYYY.M.PATCH> \
|
||||
--release-tag v<YYYY.M.PATCH> \
|
||||
--check-github
|
||||
```
|
||||
- add one `--release-tag` for every beta and stable page in the train; a
|
||||
`### Release verification` tail is permitted, but any other body drift
|
||||
fails the check; the GitHub body must begin with the complete
|
||||
`## YYYY.M.PATCH` changelog section, including its heading
|
||||
- `git diff --check`
|
||||
- for docs/changelog-only changes, no broad tests are required
|
||||
- commit with `scripts/committer "docs(changelog): refresh YYYY.M.PATCH notes" CHANGELOG.md`
|
||||
- push, pull/rebase if needed, then branch/rebase release from latest `main`
|
||||
|
||||
## Quota / API Outage Rule
|
||||
|
||||
If GitHub API quota is exhausted, do not idle. Continue work that does not need
|
||||
GitHub API:
|
||||
|
||||
- local changelog rewrite and release-note extraction
|
||||
- local pretag checks and package/build sanity
|
||||
- git push/tag checks over git protocol
|
||||
- npm registry `npm view` checks
|
||||
- exact workflow-dispatch command preparation
|
||||
|
||||
Only GitHub Release creation, workflow dispatch, run polling, artifact download,
|
||||
and issue/PR mutation need API quota.
|
||||
@@ -1,443 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
|
||||
const repo = "openclaw/openclaw";
|
||||
const excludedHandles = new Set(["openclaw", "clawsweeper", "codex", "steipete"]);
|
||||
|
||||
function fail(message) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
releaseTags: [],
|
||||
checkGithub: false,
|
||||
json: false,
|
||||
writeLedger: false,
|
||||
};
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--check-github" || arg === "--json" || arg === "--write-ledger") {
|
||||
options[
|
||||
arg === "--check-github"
|
||||
? "checkGithub"
|
||||
: arg === "--write-ledger"
|
||||
? "writeLedger"
|
||||
: "json"
|
||||
] = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--base" || arg === "--target" || arg === "--version" || arg === "--release-tag") {
|
||||
const value = argv[index + 1];
|
||||
if (!value || value.startsWith("--")) {
|
||||
fail(`missing value for ${arg}`);
|
||||
}
|
||||
if (arg === "--release-tag") {
|
||||
options.releaseTags.push(value);
|
||||
} else {
|
||||
options[arg.slice(2)] = value;
|
||||
}
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
fail(`unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
for (const name of ["base", "target", "version"]) {
|
||||
if (!options[name]) {
|
||||
fail(`--${name} is required`);
|
||||
}
|
||||
}
|
||||
if (options.checkGithub && options.releaseTags.length === 0) {
|
||||
fail("--check-github requires at least one --release-tag");
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function run(command, args) {
|
||||
return execFileSync(command, args, {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, NO_COLOR: "1" },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
}
|
||||
|
||||
function git(args) {
|
||||
return run("git", args).trimEnd();
|
||||
}
|
||||
|
||||
function githubApi(args) {
|
||||
try {
|
||||
return JSON.parse(run("ghx", ["api", ...args]).replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, ""));
|
||||
} catch (error) {
|
||||
if (typeof error.stdout === "string" && error.stdout.trim() !== "") {
|
||||
return JSON.parse(error.stdout.replace(/\u001B\[[0-?]*[ -/]*[@-~]/g, ""));
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function isEligibleHandle(handle) {
|
||||
return Boolean(handle) && !handle.endsWith("[bot]") && !excludedHandles.has(handle.toLowerCase());
|
||||
}
|
||||
|
||||
function sectionFor(changelog, version) {
|
||||
const heading = new RegExp(`^## ${escapeRegExp(version)}\\r?$`, "m").exec(changelog);
|
||||
if (!heading || heading.index === undefined) {
|
||||
fail(`CHANGELOG.md does not contain ## ${version}`);
|
||||
}
|
||||
const start = heading.index;
|
||||
const bodyStart = changelog.indexOf("\n", start) + 1;
|
||||
const next = /^## /gm;
|
||||
next.lastIndex = bodyStart;
|
||||
const nextHeading = next.exec(changelog);
|
||||
const end = nextHeading?.index ?? changelog.length;
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
source: changelog.slice(start, end).trimEnd(),
|
||||
body: changelog.slice(bodyStart, end).trim(),
|
||||
};
|
||||
}
|
||||
|
||||
function referencesIn(text) {
|
||||
return [...text.matchAll(/#(\d+)/g)].map((match) => Number(match[1]));
|
||||
}
|
||||
|
||||
function appendReferences(references, additions) {
|
||||
const seen = new Set(references);
|
||||
for (const number of additions) {
|
||||
if (!seen.has(number)) {
|
||||
references.push(number);
|
||||
seen.add(number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sourceCommits(base, target) {
|
||||
const mergeBase = git(["merge-base", base, target]);
|
||||
const output = git([
|
||||
"log",
|
||||
"--first-parent",
|
||||
"--reverse",
|
||||
"--format=%H%x1f%s%x1f%B%x1e",
|
||||
`${mergeBase}..${target}`,
|
||||
]);
|
||||
const commits = new Map();
|
||||
const revertsByTarget = new Map();
|
||||
for (const record of output.split("\x1e")) {
|
||||
if (!record) {
|
||||
continue;
|
||||
}
|
||||
const [rawHash, subject, ...bodyParts] = record.split("\x1f");
|
||||
const hash = rawHash.trim();
|
||||
const body = bodyParts.join("\x1f");
|
||||
const revertedHash = body.match(/This reverts commit ([0-9a-f]{7,40})\./i)?.[1];
|
||||
const isRevert = subject.startsWith('Revert "') || Boolean(revertedHash);
|
||||
commits.set(hash, { body, hash, isRevert, revertedHash, subject });
|
||||
}
|
||||
for (const commit of commits.values()) {
|
||||
if (!commit.revertedHash) {
|
||||
continue;
|
||||
}
|
||||
const targetHash = [...commits.keys()].find((candidate) => candidate.startsWith(commit.revertedHash));
|
||||
if (targetHash) {
|
||||
const reverts = revertsByTarget.get(targetHash) ?? [];
|
||||
reverts.push(commit.hash);
|
||||
revertsByTarget.set(targetHash, reverts);
|
||||
}
|
||||
}
|
||||
const active = new Map();
|
||||
function isActive(hash) {
|
||||
if (active.has(hash)) {
|
||||
return active.get(hash);
|
||||
}
|
||||
const cancellingReverts = revertsByTarget.get(hash) ?? [];
|
||||
const value = !cancellingReverts.some((revertHash) => isActive(revertHash));
|
||||
active.set(hash, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
const references = [];
|
||||
const revertedReferences = new Set();
|
||||
const coauthorsByReference = new Map();
|
||||
for (const commit of commits.values()) {
|
||||
if (commit.isRevert) {
|
||||
continue;
|
||||
}
|
||||
const uniqueReferences = [...new Set(referencesIn(`${commit.subject}\n${commit.body}`))];
|
||||
if (!isActive(commit.hash)) {
|
||||
for (const number of uniqueReferences) {
|
||||
revertedReferences.add(number);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
appendReferences(references, uniqueReferences);
|
||||
const coauthors = [...commit.body.matchAll(/<(?:(?:\d+)\+)?([^@<>\s]+)@users\.noreply\.github\.com>/gi)]
|
||||
.map((match) => match[1])
|
||||
.filter(isEligibleHandle);
|
||||
for (const number of uniqueReferences) {
|
||||
if (coauthors.length > 0) {
|
||||
const handles = coauthorsByReference.get(number) ?? new Set();
|
||||
for (const handle of coauthors) {
|
||||
handles.add(handle);
|
||||
}
|
||||
coauthorsByReference.set(number, handles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { mergeBase, references, revertedReferences, coauthorsByReference };
|
||||
}
|
||||
|
||||
function graphql(query) {
|
||||
return githubApi(["graphql", "-f", `query=${query}`]).data;
|
||||
}
|
||||
|
||||
function resolveReferences(numbers) {
|
||||
const nodes = new Map();
|
||||
for (let index = 0; index < numbers.length; index += 40) {
|
||||
const chunk = numbers.slice(index, index + 40);
|
||||
const fields = chunk
|
||||
.map(
|
||||
(number) => `n${number}: repository(owner: "openclaw", name: "openclaw") {
|
||||
issueOrPullRequest(number: ${number}) {
|
||||
__typename
|
||||
... on Issue { number title author { __typename login } }
|
||||
... on PullRequest { number title author { __typename login } }
|
||||
}
|
||||
}`,
|
||||
)
|
||||
.join("\n");
|
||||
const data = graphql(`query { ${fields} }`);
|
||||
for (const number of chunk) {
|
||||
const node = data[`n${number}`]?.issueOrPullRequest;
|
||||
if (node) {
|
||||
nodes.set(number, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function resolveCoauthors(handles) {
|
||||
const resolved = new Map();
|
||||
const uniqueHandles = [...new Set(handles)];
|
||||
for (let index = 0; index < uniqueHandles.length; index += 80) {
|
||||
const chunk = uniqueHandles.slice(index, index + 80);
|
||||
const fields = chunk
|
||||
.map(
|
||||
(handle, offset) =>
|
||||
`u${index + offset}: user(login: ${JSON.stringify(handle)}) { __typename login }`,
|
||||
)
|
||||
.join("\n");
|
||||
const data = graphql(`query { ${fields} }`);
|
||||
for (let offset = 0; offset < chunk.length; offset += 1) {
|
||||
const user = data[`u${index + offset}`];
|
||||
if (user?.__typename === "User" && isEligibleHandle(user.login)) {
|
||||
resolved.set(chunk[offset].toLowerCase(), user.login);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function thanksFor(node, coauthorHandles) {
|
||||
const handles = [];
|
||||
if (node.author?.__typename === "User" && isEligibleHandle(node.author.login)) {
|
||||
handles.push(node.author.login);
|
||||
}
|
||||
for (const handle of coauthorHandles) {
|
||||
if (!handles.some((candidate) => candidate.toLowerCase() === handle.toLowerCase())) {
|
||||
handles.push(handle);
|
||||
}
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
function ledgerFor(base, target, references, nodes, coauthorsByReference, resolvedCoauthors) {
|
||||
const missing = references.filter((number) => !nodes.has(number));
|
||||
if (missing.length > 0) {
|
||||
fail(`GitHub could not resolve source references: ${missing.map((number) => `#${number}`).join(", ")}`);
|
||||
}
|
||||
|
||||
const entries = references.map((number) => {
|
||||
const node = nodes.get(number);
|
||||
const rawCoauthors = coauthorsByReference.get(number) ?? new Set();
|
||||
const coauthors = [...rawCoauthors]
|
||||
.map((handle) => resolvedCoauthors.get(handle.toLowerCase()))
|
||||
.filter(Boolean);
|
||||
return {
|
||||
number,
|
||||
title: node.title.replace(/#(\d+)/g, "issue $1").replace(/\s+/g, " ").trim(),
|
||||
type: node.__typename,
|
||||
thanks: thanksFor(node, coauthors),
|
||||
};
|
||||
});
|
||||
|
||||
const pullRequests = entries.filter((entry) => entry.type === "PullRequest");
|
||||
const issues = entries.filter((entry) => entry.type === "Issue");
|
||||
const renderEntry = (entry, issue = false) => {
|
||||
const attribution = entry.thanks.length > 0 ? ` Thanks ${entry.thanks.map((handle) => `@${handle}`).join(" and ")}.` : "";
|
||||
return `- ${issue ? "Reported: " : ""}${entry.title} (#${entry.number}).${attribution}`;
|
||||
};
|
||||
const ledger = [
|
||||
"### Complete contribution ledger",
|
||||
"",
|
||||
`This audited record covers the complete ${base}..${target} history: ${pullRequests.length} PRs and ${issues.length} linked issues. The grouped notes above prioritize user impact; this ledger preserves every contribution reference and eligible human credit.`,
|
||||
"",
|
||||
"#### Pull requests",
|
||||
"",
|
||||
...pullRequests.map((entry) => renderEntry(entry)),
|
||||
"",
|
||||
"#### Linked issues",
|
||||
"",
|
||||
...issues.map((entry) => renderEntry(entry, true)),
|
||||
].join("\n");
|
||||
return { entries, issues, ledger, pullRequests };
|
||||
}
|
||||
|
||||
function replaceLedger(changelog, section, ledger) {
|
||||
const beforeLedger = section.source.replace(/\n+### Complete contribution ledger[\s\S]*$/m, "").trimEnd();
|
||||
const replacement = `${beforeLedger}\n\n${ledger}\n`;
|
||||
return `${changelog.slice(0, section.start)}${replacement}${changelog.slice(section.end)}`;
|
||||
}
|
||||
|
||||
function ledgerChecks(section, entries) {
|
||||
const errors = [];
|
||||
if (!section.source.includes("### Highlights")) {
|
||||
errors.push("missing ### Highlights");
|
||||
}
|
||||
if (!section.source.includes("### Changes")) {
|
||||
errors.push("missing ### Changes");
|
||||
}
|
||||
if (!section.source.includes("### Fixes")) {
|
||||
errors.push("missing ### Fixes");
|
||||
}
|
||||
const ledgerStart = section.source.indexOf("### Complete contribution ledger");
|
||||
if (ledgerStart < 0) {
|
||||
errors.push("missing ### Complete contribution ledger");
|
||||
return errors;
|
||||
}
|
||||
const ledger = section.source.slice(ledgerStart);
|
||||
const entryNumbers = new Set(entries.map((entry) => entry.number));
|
||||
for (const number of new Set(referencesIn(section.source))) {
|
||||
if (!entryNumbers.has(number)) {
|
||||
errors.push(`missing ledger entry for #${number}`);
|
||||
}
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const prefix = entry.type === "Issue" ? "- Reported: " : "- ";
|
||||
const line = ledger
|
||||
.split("\n")
|
||||
.find((candidate) => candidate.startsWith(prefix) && candidate.includes(`(#${entry.number})`));
|
||||
if (!line) {
|
||||
errors.push(`missing ledger entry for #${entry.number}`);
|
||||
continue;
|
||||
}
|
||||
for (const handle of entry.thanks) {
|
||||
if (!line.toLowerCase().includes(`@${handle.toLowerCase()}`)) {
|
||||
errors.push(`missing Thanks @${handle} for #${entry.number}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
function releaseChecks(section, releaseTags) {
|
||||
const expected = section.source;
|
||||
const checks = [];
|
||||
for (const tag of releaseTags) {
|
||||
const release = githubApi([`repos/${repo}/releases/tags/${encodeURIComponent(tag)}`]);
|
||||
const suffix = release.body.slice(expected.length).trimStart();
|
||||
const matches =
|
||||
release.body === expected ||
|
||||
(release.body.startsWith(expected) && (suffix === "" || suffix.startsWith("### Release verification")));
|
||||
checks.push({
|
||||
tag,
|
||||
releaseId: release.id,
|
||||
matches,
|
||||
bodyLength: release.body.length,
|
||||
});
|
||||
}
|
||||
return checks;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
let changelog = readFileSync("CHANGELOG.md", "utf8");
|
||||
let section = sectionFor(changelog, options.version);
|
||||
const source = sourceCommits(options.base, options.target);
|
||||
const preexistingNotes = section.source.replace(/\n+### Complete contribution ledger[\s\S]*$/m, "");
|
||||
const noteReferences = referencesIn(preexistingNotes);
|
||||
const revertedNoteReferences = noteReferences.filter((number) => source.revertedReferences.has(number));
|
||||
if (revertedNoteReferences.length > 0) {
|
||||
fail(
|
||||
`release notes reference reverted work: ${[
|
||||
...new Set(revertedNoteReferences),
|
||||
]
|
||||
.map((number) => `#${number}`)
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
const references = [...source.references];
|
||||
appendReferences(references, noteReferences);
|
||||
const nodes = resolveReferences(references);
|
||||
const coauthorHandles = [...source.coauthorsByReference.values()].flatMap((handles) => [...handles]);
|
||||
const resolvedCoauthors = resolveCoauthors(coauthorHandles);
|
||||
const ledger = ledgerFor(
|
||||
options.base,
|
||||
options.target,
|
||||
references,
|
||||
nodes,
|
||||
source.coauthorsByReference,
|
||||
resolvedCoauthors,
|
||||
);
|
||||
|
||||
if (options.writeLedger) {
|
||||
changelog = replaceLedger(changelog, section, ledger.ledger);
|
||||
writeFileSync("CHANGELOG.md", changelog);
|
||||
section = sectionFor(changelog, options.version);
|
||||
}
|
||||
|
||||
const errors = ledgerChecks(section, ledger.entries);
|
||||
const github = options.checkGithub ? releaseChecks(section, options.releaseTags) : [];
|
||||
for (const check of github) {
|
||||
if (!check.matches) {
|
||||
errors.push(`GitHub release ${check.tag} does not match the ${options.version} CHANGELOG section`);
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
base: options.base,
|
||||
target: options.target,
|
||||
mergeBase: source.mergeBase,
|
||||
version: options.version,
|
||||
source: {
|
||||
references: references.length,
|
||||
pullRequests: ledger.pullRequests.length,
|
||||
issues: ledger.issues.length,
|
||||
},
|
||||
github,
|
||||
errors,
|
||||
};
|
||||
if (options.json) {
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
} else {
|
||||
process.stdout.write(
|
||||
`${options.version}: ${ledger.pullRequests.length} PRs, ${ledger.issues.length} issues, ${errors.length === 0 ? "verified" : `${errors.length} errors`}\n`,
|
||||
);
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: openclaw-ghsa-maintainer
|
||||
description: "Inspect, patch, validate, publish, or confirm OpenClaw GHSA security advisories and private-fork state."
|
||||
description: Inspect, patch, validate, publish, or confirm OpenClaw GHSA security advisories and private-fork state.
|
||||
---
|
||||
|
||||
# OpenClaw GHSA Maintainer
|
||||
@@ -85,4 +85,3 @@ jq -r .description < /tmp/ghsa.refetch.json | rg '\\\\n'
|
||||
- Publishing fails with HTTP 422 if required fields are missing or the private fork still has open PRs.
|
||||
- A payload that looks correct in shell can still be wrong if Markdown was assembled with escaped newline strings.
|
||||
- Advisory PATCH sequencing matters; separate field updates when GHSA API constraints require it.
|
||||
- Public hardening/no-publish comments and draft text should avoid raw commit hashes, PR titles/numbers, and fix-mechanism summaries. Prefer patched-version fields or release-only wording; keep SHAs, PRs, and implementation notes in internal evidence.
|
||||
|
||||
@@ -168,56 +168,21 @@ Output only qualifying candidates, with: ref, surface, proof, cause, fix sketch,
|
||||
|
||||
- Start every PR review with 1-3 plain sentences explaining what the change does and why it matters. Put this before `Findings`.
|
||||
- Then list findings first. If none, say `No blocking findings` or `No findings`.
|
||||
- Show size near the top as `LOC: +<additions>/-<deletions> (<changedFiles> files)`, using live PR stats or local diff stats.
|
||||
- Always answer: bug/behavior being fixed, PR/issue URL and affected surface, provenance for regressions when traceable, and best-fix verdict.
|
||||
- For bug/regression fixes, include a compact `Provenance:` line after cause/root-cause when a bounded history pass can identify it. Use `git log -S/-G`, `git blame`, linked PRs/issues, and tests.
|
||||
- Provenance must separate roles when they differ: blamed code author username, blamed PR author username, blamed PR merger/committer username, automerge trigger when known, current PR author username, PR number, and date. Do not collapse them into one "introduced by" actor.
|
||||
- If the blamed PR was merged by `clawsweeper[bot]` or another automation, identify the human trigger when practical. Check live PR timeline/comments first; if rate-limited, use gitcrawl/cache or public PR HTML. Look for maintainer command comments such as `@clawsweeper automerge`, `/landpr`, labels/events that armed automerge, and ClawSweeper status comments. Report `automerge triggered by @login`; if not found, say trigger unknown rather than naming the bot as the human decision-maker.
|
||||
- Provenance must separate roles when they differ: blamed code author username, blamed PR merger/committer username, current PR author username, PR number, and date. Do not collapse them into one "introduced by" actor.
|
||||
- For any confirmed bug, run `git blame` on the implicated line(s) after identifying the root cause. Report who broke it as the blamed PR merger/committer, and also name the blamed code author. Include the PR number. If no PR is traceable, use the blamed commit as the provenance: commit SHA, date, and author username. Do not guess a merger or frame missing PR metadata as a separate finding.
|
||||
- Phrase provenance as `introduced by`, `made visible by`, or `carried forward by`, with confidence (`clear`, `likely`, `unknown`). If unclear, say what evidence is missing instead of guessing. For features, docs, and refactors, use `Provenance: N/A` or omit it when no broken behavior is being fixed.
|
||||
- Keep summaries compact, but include enough proof that the verdict is auditable without rereading the PR.
|
||||
|
||||
LOC proof:
|
||||
|
||||
```bash
|
||||
gh pr view <number> --json additions,deletions,changedFiles \
|
||||
--jq '"LOC: +\(.additions)/-\(.deletions) (\(.changedFiles) files)"'
|
||||
```
|
||||
|
||||
## Read beyond the diff
|
||||
|
||||
- Review the surrounding code path, not just changed lines. Open the caller, callee, data contracts, adjacent tests, and owner module.
|
||||
- Before any verdict, read enough code to fill this map: changed surface, runtime entry point, owner boundary, one caller, one callee, sibling implementations sharing the invariant, adjacent tests, current `main` behavior, and shipped/dependency/Codex contracts when relevant.
|
||||
- For large-codebase PRs, sample enough related files to understand the runtime boundary before deciding. Default to more code reading when the change touches agents, gateway, plugins, auth, sessions, process, config, or provider/runtime seams.
|
||||
- Compare the PR against current `origin/main` behavior. Check whether recent main already changed the same surface.
|
||||
- Dependency-backed behavior: MUST read upstream docs/source/types before judging API use, defaults, output shapes, errors, timeouts, memory behavior, or compatibility. Do not assume dependency contracts from memory or PR text.
|
||||
- Judge solution quality, not only correctness. Ask whether the PR is the clean owner-boundary fix or a wart/workaround that should be replaced by a small refactor, moved seam, contract change, or deletion of duplicate logic.
|
||||
- Mention the main files read when the verdict depends on code-path evidence.
|
||||
- If the user challenges the verdict or asks whether the idea is really good, resume code reading first. Do not defend, soften, or reverse the verdict until the missing caller/callee/sibling/dependency path is checked.
|
||||
|
||||
## Best-fix review loop
|
||||
|
||||
Every PR review must explicitly answer: "Is this the best fix, or only a plausible fix?"
|
||||
|
||||
Before verdict:
|
||||
|
||||
1. Reconstruct the bug, feature need, or behavior claim from issue/PR/proof.
|
||||
2. Trace current behavior from entry point to failure or decision point.
|
||||
3. Read touched files, callers, callees, owner modules, adjacent tests, and relevant docs.
|
||||
4. Read sibling surfaces that should share the invariant or could be broken by a one-sided fix.
|
||||
5. Compare against current `origin/main` and shipped behavior when regression/compat matters.
|
||||
6. Inspect upstream dependency/Codex source or docs for dependency-backed behavior.
|
||||
7. Identify at least one alternative fix location or shape, then reject it with evidence.
|
||||
8. If any required path above is uninspected, keep reading or mark `Remaining uncertainty`; do not call the PR best, blocked, proof-sufficient, or merge-ready.
|
||||
|
||||
Review output must include:
|
||||
|
||||
- `Best-fix verdict:` best / acceptable mitigation / wrong layer / too narrow / too broad.
|
||||
- `Alternatives considered:` 1-3 concrete alternatives and why rejected.
|
||||
- `Code read:` compact list of main files/contracts checked.
|
||||
- `Remaining uncertainty:` what was not proven.
|
||||
|
||||
If the best-fix answer is only "maybe", keep reading or state the missing evidence. Do not call proof sufficient until the best-fix judgment is explicit.
|
||||
|
||||
## Enforce the bug-fix evidence bar
|
||||
|
||||
@@ -229,7 +194,7 @@ If the best-fix answer is only "maybe", keep reading or state the missing eviden
|
||||
- Before landing, require:
|
||||
1. symptom evidence such as a repro, logs, or a failing test
|
||||
2. a verified root cause in code with file/line
|
||||
3. blame-backed provenance for regressions when traceable, including blamed PR merger and automerge trigger when known, or commit SHA/date when no PR is traceable
|
||||
3. blame-backed provenance for regressions when traceable, including blamed PR merger and date, or commit SHA/date when no PR is traceable
|
||||
4. a fix that touches the implicated code path
|
||||
5. a regression test when feasible, or explicit manual verification plus a reason no test was added
|
||||
- If the claim is unsubstantiated or likely wrong, request evidence or changes instead of merging.
|
||||
@@ -284,7 +249,7 @@ gh search issues --repo openclaw/openclaw --match title,body --limit 50 \
|
||||
- If bot review conversations exist on your PR, address them and resolve them yourself once fixed.
|
||||
- Leave a review conversation unresolved only when reviewer or maintainer judgment is still needed.
|
||||
- Before landing any PR with non-trivial code changes, run `$autoreview` until no accepted/actionable findings remain, unless equivalent manual review already covered it, the change is trivial/docs-only, or the user opts out.
|
||||
- When an agent is landing or merging a PR targeting `main`, use only the repo-native `scripts/pr` wrapper: run `scripts/pr review-init <PR>`, follow its emitted checkout/guard guidance, initialize and complete review artifacts with `scripts/pr review-artifacts-init <PR>`, validate them with `scripts/pr review-validate-artifacts <PR>`, then run `scripts/pr prepare-run <PR>` and `scripts/pr merge-run <PR>`.
|
||||
- When landing or merging any PR, follow the global `/landpr` process.
|
||||
- Use `scripts/committer "<msg>" <file...>` for scoped commits instead of manual `git add` and `git commit`.
|
||||
- Keep commit messages concise and action-oriented.
|
||||
- Group related changes; avoid bundling unrelated refactors.
|
||||
|
||||
@@ -13,7 +13,7 @@ Use this skill for `qa-lab` / `qa-channel` work. Repo-local QA only.
|
||||
- `docs/help/testing.md`
|
||||
- `docs/channels/qa-channel.md`
|
||||
- `qa/README.md`
|
||||
- `qa/scenarios/index.yaml`
|
||||
- `qa/scenarios/index.md`
|
||||
- `extensions/qa-lab/src/suite.ts`
|
||||
- `extensions/qa-lab/src/character-eval.ts`
|
||||
|
||||
@@ -198,9 +198,7 @@ pnpm openclaw qa character-eval \
|
||||
- Judges default to `openai/gpt-5.4,thinking=xhigh,fast` and `anthropic/claude-opus-4-6,thinking=high`.
|
||||
- Report includes judge ranking, run stats, durations, and full transcripts; do not include raw judge replies. Duration is benchmark context, not a grading signal.
|
||||
- Candidate and judge concurrency default to 16. Use `--concurrency <n>` and `--judge-concurrency <n>` to override when local gateways or provider limits need a gentler lane.
|
||||
- Scenario source is YAML-only under `qa/scenarios/`: use `index.yaml` and
|
||||
per-scenario `*.yaml` files with top-level `title`, `scenario`, and optional
|
||||
`flow`. Never add fenced `qa-scenario` / `qa-flow` Markdown files.
|
||||
- Scenario source should stay markdown-driven under `qa/scenarios/`.
|
||||
- For isolated character/persona evals, write the persona into `SOUL.md` and blank `IDENTITY.md` in the scenario flow. Use `SOUL.md + IDENTITY.md` only when intentionally testing how the normal OpenClaw identity combines with the character.
|
||||
- Keep prompts natural and task-shaped. The candidate model should receive character setup through `SOUL.md`, then normal user turns such as chat, workspace help, and small file tasks; do not ask "how would you react?" or tell the model it is in an eval.
|
||||
- Prefer at least one real task, such as creating or editing a tiny workspace artifact, so the transcript captures character under normal tool use instead of pure roleplay.
|
||||
@@ -236,8 +234,7 @@ pnpm openclaw qa manual \
|
||||
|
||||
## Repo facts
|
||||
|
||||
- Seed scenarios live in `qa/scenarios/index.yaml` and
|
||||
`qa/scenarios/<theme>/*.yaml`.
|
||||
- Seed scenarios live in `qa/`.
|
||||
- Main live runner: `extensions/qa-lab/src/suite.ts`
|
||||
- QA lab server: `extensions/qa-lab/src/lab-server.ts`
|
||||
- Child gateway harness: `extensions/qa-lab/src/gateway-child.ts`
|
||||
@@ -265,9 +262,8 @@ pnpm openclaw qa manual \
|
||||
|
||||
## When adding scenarios
|
||||
|
||||
- Add or update scenario YAML under `qa/scenarios/`; do not add `.md` scenario
|
||||
files or fenced YAML blocks.
|
||||
- Keep kickoff expectations in `qa/scenarios/index.yaml` aligned
|
||||
- Add or update scenario markdown under `qa/scenarios/`
|
||||
- Keep kickoff expectations in `qa/scenarios/index.md` aligned
|
||||
- Add executable coverage in `extensions/qa-lab/src/suite.ts`
|
||||
- Prefer end-to-end assertions over mock-only checks
|
||||
- Save outputs under `.artifacts/qa-e2e/`
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Secret scanning alert handler for OpenClaw maintainers.
|
||||
* Usage: node secret-scanning.mjs <command> [options]
|
||||
*/
|
||||
// Secret scanning alert handler for OpenClaw maintainers.
|
||||
// Usage: node secret-scanning.mjs <command> [options]
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import crypto from "node:crypto";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
@@ -41,9 +39,7 @@ function gh(args, { json = true, allowFailure = false } = {}) {
|
||||
stderr: proc.stderr,
|
||||
};
|
||||
}
|
||||
if (!json) {
|
||||
return proc.stdout;
|
||||
}
|
||||
if (!json) return proc.stdout;
|
||||
try {
|
||||
return JSON.parse(proc.stdout);
|
||||
} catch {
|
||||
@@ -59,7 +55,6 @@ function isBodyLocationType(locationType) {
|
||||
return locationType === "issue_body" || locationType === "pull_request_body";
|
||||
}
|
||||
|
||||
/** Decides whether redacting an issue/PR body requires notifying the reporter. */
|
||||
export function decideBodyRedaction(currentBody, redactedBody) {
|
||||
const bodyChanged = String(currentBody) !== String(redactedBody);
|
||||
return {
|
||||
@@ -68,7 +63,6 @@ export function decideBodyRedaction(currentBody, redactedBody) {
|
||||
};
|
||||
}
|
||||
|
||||
/** Loads redaction-result metadata for issue/PR body secret locations. */
|
||||
export function loadBodyRedactionResult(locationType, resultFile) {
|
||||
if (!isBodyLocationType(locationType)) {
|
||||
return { notify_required: true };
|
||||
@@ -76,9 +70,7 @@ export function loadBodyRedactionResult(locationType, resultFile) {
|
||||
if (!resultFile) {
|
||||
fail("Body notifications require a redaction result file from redact-body-if-needed");
|
||||
}
|
||||
if (!fs.existsSync(resultFile)) {
|
||||
fail(`File not found: ${resultFile}`);
|
||||
}
|
||||
if (!fs.existsSync(resultFile)) fail(`File not found: ${resultFile}`);
|
||||
|
||||
const result = JSON.parse(fs.readFileSync(resultFile, "utf8"));
|
||||
if (typeof result.notify_required !== "boolean") {
|
||||
@@ -190,11 +182,10 @@ function fetchDiscussionComment(discussionNumber, discussionCommentDbId) {
|
||||
failOnGraphQLFailure(gql, `Failed to fetch discussion #${discussionNumber}`);
|
||||
|
||||
const discussion = gql?.data?.repository?.discussion;
|
||||
if (!discussion) {
|
||||
if (!discussion)
|
||||
fail(
|
||||
`Discussion #${discussionNumber} not found — it may have been deleted. The alert cannot be processed via this skill.`,
|
||||
);
|
||||
}
|
||||
|
||||
discussionId = discussion.id;
|
||||
|
||||
@@ -214,18 +205,15 @@ function fetchDiscussionComment(discussionNumber, discussionCommentDbId) {
|
||||
`Failed to fetch replies for discussion comment ${topLevelComment.id}`,
|
||||
);
|
||||
const replies = replyPage?.data?.node?.replies;
|
||||
if (!replies) {
|
||||
if (!replies)
|
||||
fail(`Failed to paginate replies for discussion comment ${topLevelComment.id}`);
|
||||
}
|
||||
|
||||
reply = findDiscussionCommentNode(replies.nodes, discussionCommentDbId);
|
||||
hasMoreReplies = replies.pageInfo.hasNextPage;
|
||||
replyCursor = replies.pageInfo.endCursor;
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
return { discussionId, comment: reply };
|
||||
}
|
||||
if (reply) return { discussionId, comment: reply };
|
||||
}
|
||||
|
||||
hasNextPage = discussion.comments.pageInfo.hasNextPage;
|
||||
@@ -253,9 +241,7 @@ function createDiscussionComment(discussionNodeId, body, replyToNodeId) {
|
||||
* Fetch alert metadata + locations. Never exposes .secret.
|
||||
*/
|
||||
function cmdFetchAlert(alertNumber) {
|
||||
if (!alertNumber) {
|
||||
fail("Usage: fetch-alert <number>");
|
||||
}
|
||||
if (!alertNumber) fail("Usage: fetch-alert <number>");
|
||||
|
||||
const alert = gh(["api", `repos/${REPO}/secret-scanning/alerts/${alertNumber}?hide_secret=true`]);
|
||||
|
||||
@@ -294,23 +280,17 @@ function cmdFetchAlert(alertNumber) {
|
||||
* Saves full body to a temp file. Prints metadata + file path to stdout.
|
||||
*/
|
||||
function cmdFetchContent(locationJson) {
|
||||
if (!locationJson) {
|
||||
fail("Usage: fetch-content '<location-json>'");
|
||||
}
|
||||
if (!locationJson) fail("Usage: fetch-content '<location-json>'");
|
||||
const location = JSON.parse(locationJson);
|
||||
const type = location.type;
|
||||
const details = location.details;
|
||||
|
||||
if (type === "discussion_comment") {
|
||||
const commentUrl = details.discussion_comment_url;
|
||||
if (!commentUrl) {
|
||||
fail("No discussion_comment_url in location details");
|
||||
}
|
||||
if (!commentUrl) fail("No discussion_comment_url in location details");
|
||||
|
||||
const urlMatch = commentUrl.match(/discussions\/(\d+)#discussioncomment-(\d+)/);
|
||||
if (!urlMatch) {
|
||||
fail(`Cannot parse discussion comment URL: ${commentUrl}`);
|
||||
}
|
||||
if (!urlMatch) fail(`Cannot parse discussion comment URL: ${commentUrl}`);
|
||||
const discussionNumber = urlMatch[1];
|
||||
const discussionCommentDbId = urlMatch[2];
|
||||
|
||||
@@ -318,11 +298,10 @@ function cmdFetchContent(locationJson) {
|
||||
discussionNumber,
|
||||
discussionCommentDbId,
|
||||
);
|
||||
if (!comment) {
|
||||
if (!comment)
|
||||
fail(
|
||||
`Discussion comment #${discussionCommentDbId} not found in discussion #${discussionNumber}`,
|
||||
);
|
||||
}
|
||||
|
||||
const bodyFile = tmpFile("body.md");
|
||||
fs.writeFileSync(bodyFile, comment.body || "");
|
||||
@@ -355,9 +334,7 @@ function cmdFetchContent(locationJson) {
|
||||
details.issue_comment_url ||
|
||||
details.pull_request_comment_url ||
|
||||
details.pull_request_review_comment_url;
|
||||
if (!commentUrl) {
|
||||
fail(`No comment URL in location details`);
|
||||
}
|
||||
if (!commentUrl) fail(`No comment URL in location details`);
|
||||
|
||||
const comment = gh(["api", commentUrl]);
|
||||
const bodyFile = tmpFile("body.md");
|
||||
@@ -401,9 +378,7 @@ function cmdFetchContent(locationJson) {
|
||||
);
|
||||
} else if (type === "issue_body") {
|
||||
const issueUrl = details.issue_body_url || details.issue_url;
|
||||
if (!issueUrl) {
|
||||
fail("No issue URL in location details");
|
||||
}
|
||||
if (!issueUrl) fail("No issue URL in location details");
|
||||
|
||||
const issue = gh(["api", issueUrl]);
|
||||
const bodyFile = tmpFile("body.md");
|
||||
@@ -439,9 +414,7 @@ function cmdFetchContent(locationJson) {
|
||||
);
|
||||
} else if (type === "pull_request_body") {
|
||||
const prUrl = details.pull_request_body_url || details.pull_request_url;
|
||||
if (!prUrl) {
|
||||
fail("No PR URL in location details");
|
||||
}
|
||||
if (!prUrl) fail("No PR URL in location details");
|
||||
|
||||
const pr = gh(["api", prUrl]);
|
||||
const bodyFile = tmpFile("body.md");
|
||||
@@ -517,9 +490,7 @@ function cmdRedactBody(kind, number, bodyFile) {
|
||||
if (!kind || !number || !bodyFile) {
|
||||
fail("Usage: redact-body <issue|pr> <number> <redacted-body-file>");
|
||||
}
|
||||
if (!fs.existsSync(bodyFile)) {
|
||||
fail(`File not found: ${bodyFile}`);
|
||||
}
|
||||
if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
|
||||
|
||||
const endpoint =
|
||||
kind === "pr" ? `repos/${REPO}/pulls/${number}` : `repos/${REPO}/issues/${number}`;
|
||||
@@ -538,12 +509,8 @@ function cmdRedactBodyIfNeeded(kind, number, currentBodyFile, redactedBodyFile,
|
||||
"Usage: redact-body-if-needed <issue|pr> <number> <current-body-file> <redacted-body-file> <result-file>",
|
||||
);
|
||||
}
|
||||
if (!fs.existsSync(currentBodyFile)) {
|
||||
fail(`File not found: ${currentBodyFile}`);
|
||||
}
|
||||
if (!fs.existsSync(redactedBodyFile)) {
|
||||
fail(`File not found: ${redactedBodyFile}`);
|
||||
}
|
||||
if (!fs.existsSync(currentBodyFile)) fail(`File not found: ${currentBodyFile}`);
|
||||
if (!fs.existsSync(redactedBodyFile)) fail(`File not found: ${redactedBodyFile}`);
|
||||
|
||||
const currentBody = fs.readFileSync(currentBodyFile, "utf8");
|
||||
const redactedBody = fs.readFileSync(redactedBodyFile, "utf8");
|
||||
@@ -574,9 +541,7 @@ function cmdRedactBodyIfNeeded(kind, number, currentBodyFile, redactedBodyFile,
|
||||
* Delete a comment (and all its edit history).
|
||||
*/
|
||||
function cmdDeleteComment(commentId) {
|
||||
if (!commentId) {
|
||||
fail("Usage: delete-comment <comment-id>");
|
||||
}
|
||||
if (!commentId) fail("Usage: delete-comment <comment-id>");
|
||||
gh(["api", `repos/${REPO}/issues/comments/${commentId}`, "-X", "DELETE"], { json: false });
|
||||
console.log(JSON.stringify({ ok: true, deleted_comment_id: Number(commentId) }));
|
||||
}
|
||||
@@ -586,9 +551,7 @@ function cmdDeleteComment(commentId) {
|
||||
* Delete a discussion comment via GraphQL (and all its edit history).
|
||||
*/
|
||||
function cmdDeleteDiscussionComment(nodeId) {
|
||||
if (!nodeId) {
|
||||
fail("Usage: delete-discussion-comment <node-id>");
|
||||
}
|
||||
if (!nodeId) fail("Usage: delete-discussion-comment <node-id>");
|
||||
const result = ghGraphQL(
|
||||
`mutation { deleteDiscussionComment(input: { id: "${nodeId}" }) { comment { id } } }`,
|
||||
);
|
||||
@@ -603,12 +566,9 @@ function cmdDeleteDiscussionComment(nodeId) {
|
||||
* Create a new discussion comment via GraphQL.
|
||||
*/
|
||||
function cmdRecreateDiscussionComment(discussionNodeId, bodyFile, replyToNodeId) {
|
||||
if (!discussionNodeId || !bodyFile) {
|
||||
if (!discussionNodeId || !bodyFile)
|
||||
fail("Usage: recreate-discussion-comment <discussion-node-id> <body-file> [reply-to-node-id]");
|
||||
}
|
||||
if (!fs.existsSync(bodyFile)) {
|
||||
fail(`File not found: ${bodyFile}`);
|
||||
}
|
||||
if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
|
||||
|
||||
const body = fs.readFileSync(bodyFile, "utf8");
|
||||
const newComment = createDiscussionComment(discussionNodeId, body, replyToNodeId);
|
||||
@@ -626,12 +586,8 @@ function cmdRecreateDiscussionComment(discussionNodeId, bodyFile, replyToNodeId)
|
||||
* Create a new comment from a file.
|
||||
*/
|
||||
function cmdRecreateComment(issueNumber, bodyFile) {
|
||||
if (!issueNumber || !bodyFile) {
|
||||
fail("Usage: recreate-comment <issue-number> <body-file>");
|
||||
}
|
||||
if (!fs.existsSync(bodyFile)) {
|
||||
fail(`File not found: ${bodyFile}`);
|
||||
}
|
||||
if (!issueNumber || !bodyFile) fail("Usage: recreate-comment <issue-number> <body-file>");
|
||||
if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
|
||||
|
||||
const result = gh([
|
||||
"api",
|
||||
@@ -759,9 +715,7 @@ function cmdNotify(target, author, locationType, secretTypes, replyToNodeId) {
|
||||
* Close a secret scanning alert.
|
||||
*/
|
||||
function cmdResolve(alertNumber, resolution, comment) {
|
||||
if (!alertNumber) {
|
||||
fail("Usage: resolve <alert-number> [resolution] [comment]");
|
||||
}
|
||||
if (!alertNumber) fail("Usage: resolve <alert-number> [resolution] [comment]");
|
||||
|
||||
const res = resolution || "revoked";
|
||||
const resComment = comment || "Content redacted and author notified to rotate credentials.";
|
||||
@@ -819,12 +773,8 @@ function cmdListOpen() {
|
||||
* Print a formatted summary table from a JSON results file.
|
||||
*/
|
||||
function cmdSummary(jsonFile) {
|
||||
if (!jsonFile) {
|
||||
fail("Usage: summary <json-file>");
|
||||
}
|
||||
if (!fs.existsSync(jsonFile)) {
|
||||
fail(`File not found: ${jsonFile}`);
|
||||
}
|
||||
if (!jsonFile) fail("Usage: summary <json-file>");
|
||||
if (!fs.existsSync(jsonFile)) fail(`File not found: ${jsonFile}`);
|
||||
|
||||
const results = JSON.parse(fs.readFileSync(jsonFile, "utf8"));
|
||||
const lines = [];
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Heap snapshot diff utility for OpenClaw test memory leak investigations.
|
||||
*/
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -19,7 +19,7 @@ or validating a change without wasting hours.
|
||||
Prove the touched surface first. Do not reflexively run the whole suite.
|
||||
|
||||
1. Inspect the diff and classify the touched surface:
|
||||
- normal source checkout, source change: `pnpm changed:lanes --json`, then `pnpm check:changed` (delegates to Crabbox/Testbox)
|
||||
- normal source checkout, source change: `pnpm changed:lanes --json`, then `pnpm check:changed`
|
||||
- normal source checkout, tests only: `pnpm test:changed`
|
||||
- normal source checkout, one failing file: `pnpm test <path-or-filter> -- --reporter=verbose`
|
||||
- Codex worktree or linked/sparse checkout, one/few explicit files: `node scripts/run-vitest.mjs <path-or-filter>`
|
||||
@@ -27,7 +27,7 @@ Prove the touched surface first. Do not reflexively run the whole suite.
|
||||
use the Crabbox wrapper with the provider that matches the proof surface.
|
||||
For maintainer heavy `pnpm` gates, that is usually delegated Blacksmith
|
||||
Testbox through Crabbox, e.g. `node scripts/crabbox-wrapper.mjs run
|
||||
--provider blacksmith-testbox ... -- env OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed`. For direct AWS
|
||||
--provider blacksmith-testbox ... -- pnpm check:changed`. For direct AWS
|
||||
Crabbox proof, omit `--provider` and let `.crabbox.yaml` choose AWS.
|
||||
- workflow-only: `git diff --check`, workflow syntax/lint (`actionlint` when available)
|
||||
- docs-only: `pnpm docs:list`, docs formatter/lint only if docs tooling changed or requested
|
||||
@@ -66,18 +66,15 @@ scripts/crabbox-wrapper.mjs` for Testbox, and `git commit --no-verify` only
|
||||
|
||||
```bash
|
||||
pnpm changed:lanes --json
|
||||
pnpm check:changed # Crabbox/Testbox changed typecheck/lint/guards; no Vitest
|
||||
pnpm check:changed # changed typecheck/lint/guards; no Vitest
|
||||
pnpm test:changed # cheap smart changed Vitest targets
|
||||
pnpm verify # full check, then full Vitest
|
||||
OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed
|
||||
pnpm test <path-or-filter> -- --reporter=verbose
|
||||
OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test <path-or-filter>
|
||||
```
|
||||
|
||||
Use targeted file paths whenever possible. Avoid raw `vitest`; use the repo
|
||||
`pnpm test` wrapper so project routing, workers, and setup stay correct. If raw
|
||||
Vitest is unavoidable, use `vitest run ...`; bare `vitest ...` starts local watch
|
||||
mode and will not exit on its own.
|
||||
`pnpm test` wrapper so project routing, workers, and setup stay correct.
|
||||
When the checkout is a Codex worktree, prefer the direct node harness instead:
|
||||
|
||||
```bash
|
||||
@@ -92,8 +89,6 @@ status checks or install reconciliation in a linked worktree.
|
||||
- `pnpm check` and `pnpm check:changed` do not run Vitest tests. They are for
|
||||
typecheck, lint, and guard proof.
|
||||
- `pnpm test` and `pnpm test:changed` run Vitest tests.
|
||||
- `pnpm verify` runs `pnpm check`, then `pnpm test`, with Crabbox phase markers
|
||||
so remote summaries show which half failed.
|
||||
- `pnpm test:changed` is intentionally cheap by default: direct test edits,
|
||||
sibling tests, explicit source mappings, and import-graph dependents.
|
||||
- `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed` is the explicit broad
|
||||
@@ -215,7 +210,7 @@ workflow only spends setup and queue time on that suite.
|
||||
### Release Evidence
|
||||
|
||||
After release-candidate validation or before a release decision, record the
|
||||
important run ids in the public `openclaw/releases` evidence ledger.
|
||||
important run ids in the private `openclaw/releases-private` evidence ledger.
|
||||
Use the manual `OpenClaw Release Evidence`
|
||||
(`openclaw-release-evidence.yml`) workflow there. It writes durable summaries
|
||||
under `evidence/<release-id>/` and commits:
|
||||
@@ -238,13 +233,13 @@ short release-manager notes there. Do not store raw logs, provider
|
||||
prompts/responses, channel transcripts, signing material, or secret-bearing
|
||||
config in git; raw logs stay in Actions artifacts.
|
||||
|
||||
When `Full Release Validation` completes and `OPENCLAW_RELEASES_DISPATCH_TOKEN`
|
||||
is configured in the source repo, it requests the public
|
||||
`OpenClaw Release Evidence From Full Validation` workflow. That workflow reads
|
||||
the parent full-validation run, extracts the child CI/release-checks/Telegram
|
||||
run ids from the parent logs, and opens the evidence PR automatically. If the
|
||||
token is absent or the run predates this wiring, trigger that workflow manually
|
||||
with the full-validation run id.
|
||||
When `Full Release Validation` completes and
|
||||
`OPENCLAW_RELEASES_PRIVATE_DISPATCH_TOKEN` is configured in the public repo, it
|
||||
requests the private `OpenClaw Release Evidence From Full Validation` workflow.
|
||||
That private workflow reads the parent full-validation run, extracts the child
|
||||
CI/release-checks/Telegram run ids from the parent logs, and opens the evidence
|
||||
PR automatically. If the token is absent or the run predates this wiring, trigger
|
||||
that private workflow manually with the full-validation run id.
|
||||
|
||||
### Release Checks
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
name: release-openclaw-announcement
|
||||
description: "Draft or post OpenClaw beta/stable Discord release announcements from changelog, GitHub release, registry, and validation evidence. Use when announcing a beta, stable release, release candidate, or asking what users should test after an OpenClaw release."
|
||||
---
|
||||
|
||||
# OpenClaw Release Announcement
|
||||
|
||||
Use with `release-openclaw-maintainer` after a beta or stable release is live.
|
||||
Use with `$discord-user-post` when actually posting to Discord as the logged-in
|
||||
user.
|
||||
|
||||
## Evidence First
|
||||
|
||||
Before drafting focus areas, read real release evidence:
|
||||
|
||||
1. Current GitHub release body for the tag.
|
||||
2. `CHANGELOG.md` section for the released base version.
|
||||
3. Commits since the previous shipped version or the operator-specified base.
|
||||
4. Registry/package metadata for the exact version and current dist-tag.
|
||||
5. Validation status that is relevant to user confidence.
|
||||
|
||||
Do not claim a full changelog audit unless you did it. If you only read the
|
||||
generated release notes or top changelog section, say that and either audit
|
||||
properly or draft with that limitation.
|
||||
|
||||
For beta focus areas, prioritize user-observable changes over internal test or
|
||||
CI mechanics:
|
||||
|
||||
- install/update paths
|
||||
- OS/platform-specific behavior
|
||||
- Gateway startup/restart, config, and runtime behavior
|
||||
- provider/model/runtime routing
|
||||
- plugin loading and local plugin development
|
||||
- channels and media paths
|
||||
- security/data-loss/user-impact fixes
|
||||
|
||||
Do not let late release-branch fixes automatically dominate the announcement.
|
||||
If the version includes a large delta from the previous shipped version, rank
|
||||
focus areas by the whole release delta and expected user impact; mention late
|
||||
fixes in their natural category.
|
||||
|
||||
## Required Copy
|
||||
|
||||
Every beta announcement must make beta status explicit and include:
|
||||
|
||||
- exact version, e.g. `OpenClaw 2026.5.25-beta.1`
|
||||
- one-sentence risk framing: beta, useful for testing, not stable promotion
|
||||
- focused test areas derived from evidence, not guesswork
|
||||
- update command promoted near the top:
|
||||
```sh
|
||||
openclaw update --channel beta --yes
|
||||
openclaw --version
|
||||
```
|
||||
- fresh install path:
|
||||
`Install from https://openclaw.ai`
|
||||
- GitHub release link
|
||||
- concise validation note, without making CI the headline
|
||||
|
||||
Do not suggest npm install commands in beta announcements unless the operator
|
||||
explicitly asks for npm-specific copy or troubleshooting text. It is fine to use
|
||||
registry metadata as evidence; do not turn that into public install guidance.
|
||||
|
||||
For stable announcements, use the stable channel wording:
|
||||
|
||||
```sh
|
||||
openclaw update --channel stable --yes
|
||||
openclaw --version
|
||||
```
|
||||
|
||||
Fresh installs still point to `https://openclaw.ai`.
|
||||
|
||||
## Style
|
||||
|
||||
- Discord Markdown, no tables.
|
||||
- Keep it skimmable: short intro, bullets, commands, links.
|
||||
- Lead with what users can feel or test, not proof plumbing.
|
||||
- Mention validation only after install/update instructions.
|
||||
- Be specific about where feedback is useful.
|
||||
- Do not mention private local proof paths in public announcements.
|
||||
- Do not overstate unverified platforms, channels, or provider behavior.
|
||||
|
||||
## Posting
|
||||
|
||||
When asked to post, use `$discord-user-post` to operate the logged-in Discord
|
||||
desktop app as the user. Resolve and visibly verify the exact server/channel,
|
||||
inspect the final body, and request action-time confirmation before entering or
|
||||
sending it. Never use OpenClaw channel sends, bots, webhooks, relays, or tokens.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "OpenClaw Release Announcement"
|
||||
short_description: "Draft Discord beta/stable release announcements from evidence."
|
||||
default_prompt: "Use this skill to draft an OpenClaw beta or stable Discord announcement from changelog, release notes, npm/GitHub release proof, and validation evidence."
|
||||
@@ -16,33 +16,6 @@ Use this with `$release-openclaw-maintainer` and `$openclaw-testing` when a rele
|
||||
- Watch one parent run plus compact child summaries. Avoid broad `gh run view` polling loops; REST quota is easy to burn.
|
||||
- Fetch logs only for failed or currently-blocking jobs. If quota is low, stop polling and wait for reset.
|
||||
- Treat live-provider flakes separately from code failures: prove key validity, provider HTTP status, retry evidence, and exact failing lane before editing code.
|
||||
- A model-list response proves authentication, not billing or inference
|
||||
entitlement. Mandatory live providers must pass a real completion probe
|
||||
before release dispatch. Fix the credential first; do not add an alternate
|
||||
auth path merely to bypass a failed release credential.
|
||||
- Full Release Validation parent monitors fail fast: once a required child job
|
||||
fails, the parent cancels the remaining child matrix and prints the failed
|
||||
job summary. Inspect that first red job instead of waiting for unrelated
|
||||
matrix tails.
|
||||
- In a sparse worktree or Testbox source sync, first confirm `package.json`,
|
||||
`pnpm-lock.yaml`, and every source path the selected check reads. If any are
|
||||
absent, that checkout cannot validate a release dependency or Docker lane:
|
||||
stop and use the repo remote changed gate or a full task worktree. When the
|
||||
inputs are present and a release fix changes `package.json` or
|
||||
`pnpm-lock.yaml`, rebuild only the task-owned disposable box with
|
||||
`CI=true pnpm install --frozen-lockfile`, then run an explicit
|
||||
`require.resolve()` probe before Docker or focused tests. The CI flag permits
|
||||
pnpm to recreate a prewarmed modules directory without an interactive
|
||||
confirmation. Do not weaken the lockfile or label sparse-checkout failures
|
||||
as product/Docker failures.
|
||||
- If the candidate is rebased or its base SHA changes after warmup, stop the
|
||||
task-owned box and warm a fresh one before testing. Testbox source sync is
|
||||
relative to the warmed source tree; continuing can mix an old base file with
|
||||
a new candidate diff and produce false lockfile or Docker failures.
|
||||
- For a committed release candidate, warm the box with
|
||||
`blacksmith testbox warmup ... --ref <candidate-branch-or-sha>`. Do not rely
|
||||
on source sync to overlay committed branch changes onto the workflow's
|
||||
default ref.
|
||||
|
||||
## Preflight
|
||||
|
||||
@@ -59,8 +32,6 @@ git rev-parse HEAD
|
||||
preflight. Inject those exact targeted keys first, then run the verifier; use
|
||||
ambient env only when it was already intentionally injected for this release.
|
||||
The script prints only provider status and HTTP class, never tokens.
|
||||
The Anthropic check performs a tiny message completion so exhausted or
|
||||
non-billable credentials fail before the expensive release matrix.
|
||||
|
||||
## Dispatch
|
||||
|
||||
@@ -76,7 +47,7 @@ gh workflow run openclaw-performance.yml \
|
||||
-f repeat=3 \
|
||||
-f deep_profile=false \
|
||||
-f live_openai_candidate=false \
|
||||
-f fail_on_regression=true
|
||||
-f fail_on_regression=false
|
||||
```
|
||||
|
||||
- Do not wait for full release validation to start this early perf signal.
|
||||
@@ -85,19 +56,11 @@ gh workflow run openclaw-performance.yml \
|
||||
- Call out any regression in the release proof. Treat a major regression as a
|
||||
release blocker until it is fixed, waived by the operator, or proven to be
|
||||
infrastructure noise.
|
||||
- Full Release Validation records blocking product-performance evidence. The
|
||||
early standalone run is for overlap and faster regression discovery, but a
|
||||
regression or missing child run blocks the parent validation.
|
||||
- Full Release Validation also records advisory product-performance evidence;
|
||||
the early standalone run is for overlap and faster regression discovery.
|
||||
|
||||
Prefer the trusted workflow on `main`, target the exact release SHA:
|
||||
|
||||
- Keep trusted-workflow checks compatible with frozen release targets. If
|
||||
`main` adds a target-owned guard script or package command after the release
|
||||
branch cut, make the trusted workflow skip only when that target surface is
|
||||
absent. Heal the trusted workflow before rerunning validation; do not port an
|
||||
unrelated runtime refactor or mutate the release candidate just to satisfy a
|
||||
newer `main`-only check.
|
||||
|
||||
```bash
|
||||
gh workflow run full-release-validation.yml \
|
||||
--repo openclaw/openclaw \
|
||||
@@ -109,10 +72,7 @@ gh workflow run full-release-validation.yml \
|
||||
-f rerun_group=all
|
||||
```
|
||||
|
||||
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Stable and full profiles force the release soak; the beta profile may opt in with `run_release_soak=true`. Use narrow `rerun_group` after focused fixes.
|
||||
Publish with `openclaw-release-publish.yml` using `release_profile=from-validation`
|
||||
unless a maintainer intentionally wants to cross-check a specific profile; the
|
||||
publish workflow reads the effective profile from the full-validation manifest.
|
||||
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Use narrow `rerun_group` after focused fixes.
|
||||
|
||||
## Watch
|
||||
|
||||
@@ -139,25 +99,9 @@ Stop watchers before ending the turn or switching strategy.
|
||||
--jq '.jobs[] | select(.conclusion=="failure" or .conclusion=="timed_out" or .conclusion=="cancelled") | [.databaseId,.name,.conclusion,.url] | @tsv'
|
||||
```
|
||||
3. Fetch one failed job log. If rate-limited, note reset time and avoid more REST calls.
|
||||
4. For secret-looking failures, validate a real completion from the same secret source before editing code. A successful model-list request is insufficient.
|
||||
Claude CLI subscription credentials are a separate native auth path; prove
|
||||
them in a clean-home CLI probe, never as a substitute for a required
|
||||
Anthropic API-key lane.
|
||||
4. For secret-looking failures, validate the provider endpoint from the same secret source before editing code.
|
||||
5. For live-cache failures, inspect whether it is missing/invalid key, empty text, provider refusal, timeout, or baseline miss. Do not weaken release gates without clear provider evidence.
|
||||
6. Fix narrowly, run local/changed proof, commit, push, rerun the smallest matching group.
|
||||
7. If a required PR CI run is capacity-stalled with queued jobs and no active
|
||||
jobs, do not cancel unrelated work or accept a generic manual dispatch.
|
||||
From the PR head branch, dispatch the explicit exact-SHA fallback:
|
||||
`gh workflow run ci.yml --repo openclaw/openclaw --ref <pr-head-branch> -f
|
||||
target_ref=<full-pr-sha> -f include_android=true -f release_gate=true`.
|
||||
It runs on GitHub-hosted runners and is accepted only when its run title is
|
||||
`CI release gate <full-pr-sha>`. Record the stalled Blacksmith run and the
|
||||
fallback run in release evidence.
|
||||
If `Blacksmith Build Artifacts Testbox` is the only remaining required gate
|
||||
and remains queued without a runner, that completed exact fallback may cover
|
||||
it because CI's `build-artifacts` job already builds, packages, and smoke
|
||||
tests the artifacts. Do not use this coverage after the artifact workflow
|
||||
starts or completes non-successfully.
|
||||
|
||||
## Evidence
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Release CI summary helper that prints parent and child workflow status for a
|
||||
* full release run.
|
||||
*/
|
||||
import { execFileSync } from "node:child_process";
|
||||
import process from "node:process";
|
||||
|
||||
@@ -25,30 +21,6 @@ function jsonGh(args) {
|
||||
return JSON.parse(gh(args));
|
||||
}
|
||||
|
||||
function githubRestJson(pathSuffix) {
|
||||
const result = execFileSync(
|
||||
"bash",
|
||||
[
|
||||
"-lc",
|
||||
[
|
||||
"set -euo pipefail",
|
||||
'token="$(gh auth token)"',
|
||||
'curl -fsS -H "Authorization: Bearer ${token}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "${OPENCLAW_GITHUB_REST_URL}"',
|
||||
].join("\n"),
|
||||
],
|
||||
{
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_GITHUB_REST_URL: `https://api.github.com/repos/${repo}/${pathSuffix}`,
|
||||
},
|
||||
maxBuffer: 16 * 1024 * 1024,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
return JSON.parse(result);
|
||||
}
|
||||
|
||||
function rate() {
|
||||
try {
|
||||
return jsonGh(["api", "rate_limit"]).resources.core;
|
||||
@@ -87,30 +59,12 @@ for (const job of parent.jobs ?? []) {
|
||||
}
|
||||
|
||||
const since = parent.createdAt;
|
||||
const runsQuery = new URLSearchParams({
|
||||
per_page: "100",
|
||||
created: `>=${since}`,
|
||||
exclude_pull_requests: "true",
|
||||
});
|
||||
const childWorkflowNames = new Set([
|
||||
"CI",
|
||||
"OpenClaw Release Checks",
|
||||
"Plugin Prerelease",
|
||||
"NPM Telegram Beta E2E",
|
||||
"Full Release Validation",
|
||||
]);
|
||||
const runs = githubRestJson(`actions/runs?${runsQuery.toString()}`).workflow_runs ?? [];
|
||||
const runList = runs
|
||||
.filter(
|
||||
(run) =>
|
||||
run.created_at >= since &&
|
||||
run.head_sha === parent.headSha &&
|
||||
childWorkflowNames.has(run.name),
|
||||
)
|
||||
.map((run) =>
|
||||
[run.id, run.name, run.status, run.conclusion ?? "", run.head_sha, run.html_url].join("\t"),
|
||||
)
|
||||
.join("\n");
|
||||
const runList = gh([
|
||||
"api",
|
||||
`repos/${repo}/actions/runs?per_page=100`,
|
||||
"--jq",
|
||||
`.workflow_runs[] | select(.created_at >= "${since}") | select(.name=="CI" or .name=="OpenClaw Release Checks" or .name=="Plugin Prerelease" or .name=="NPM Telegram Beta E2E" or .name=="Full Release Validation") | [.id,.name,.status,.conclusion,.head_sha,.html_url] | @tsv`,
|
||||
]).trim();
|
||||
|
||||
if (!runList) {
|
||||
console.log("children: none found yet");
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Release preflight helper that verifies required provider API keys without
|
||||
* printing secret values. Anthropic must complete a prompt because model-list
|
||||
* access does not prove billing or inference entitlement.
|
||||
*/
|
||||
import process from "node:process";
|
||||
|
||||
const args = new Map();
|
||||
for (let index = 2; index < process.argv.length; index += 1) {
|
||||
const arg = process.argv[index];
|
||||
if (!arg.startsWith("--")) {
|
||||
continue;
|
||||
}
|
||||
if (!arg.startsWith("--")) continue;
|
||||
const [key, inlineValue] = arg.slice(2).split("=", 2);
|
||||
const value = inlineValue ?? process.argv[index + 1];
|
||||
if (inlineValue === undefined) {
|
||||
index += 1;
|
||||
}
|
||||
if (inlineValue === undefined) index += 1;
|
||||
args.set(key, value);
|
||||
}
|
||||
|
||||
@@ -33,9 +24,7 @@ const timeoutMs = Number(args.get("timeout-ms") ?? 10_000);
|
||||
function envFirst(names) {
|
||||
for (const name of names) {
|
||||
const value = process.env[name]?.trim();
|
||||
if (value) {
|
||||
return { name, value };
|
||||
}
|
||||
if (value) return { name, value };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -51,19 +40,13 @@ async function checkProvider(id, config) {
|
||||
try {
|
||||
const headers = config.headers(secret.value);
|
||||
const response = await fetch(config.url, {
|
||||
body: config.body,
|
||||
headers,
|
||||
method: config.method,
|
||||
signal: controller.signal,
|
||||
});
|
||||
const responseBody = config.validateResponse
|
||||
? await response.json().catch(() => undefined)
|
||||
: undefined;
|
||||
const ok = response.ok && (!config.validateResponse || config.validateResponse(responseBody));
|
||||
return {
|
||||
id,
|
||||
ok,
|
||||
status: response.ok ? (ok ? "ok" : "invalid_response") : `http_${response.status}`,
|
||||
ok: response.ok,
|
||||
status: response.ok ? "ok" : `http_${response.status}`,
|
||||
env: secret.name,
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -86,21 +69,11 @@ const providers = {
|
||||
},
|
||||
anthropic: {
|
||||
env: ["ANTHROPIC_API_KEY", "ANTHROPIC_API_TOKEN"],
|
||||
url: "https://api.anthropic.com/v1/messages",
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
max_tokens: 8,
|
||||
messages: [{ role: "user", content: "Reply with OK." }],
|
||||
model: "claude-haiku-4-5",
|
||||
}),
|
||||
url: "https://api.anthropic.com/v1/models",
|
||||
headers: (token) => ({
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json",
|
||||
"x-api-key": token,
|
||||
}),
|
||||
validateResponse: (body) =>
|
||||
Array.isArray(body?.content) &&
|
||||
body.content.some((part) => typeof part?.text === "string" && part.text.trim()),
|
||||
},
|
||||
fireworks: {
|
||||
env: ["FIREWORKS_API_KEY"],
|
||||
@@ -131,9 +104,7 @@ let failed = false;
|
||||
for (const result of results) {
|
||||
const requiredLabel = required.has(result.id) ? "required" : "optional";
|
||||
console.log(`${result.id}: ${result.status} env=${result.env} ${requiredLabel}`);
|
||||
if (required.has(result.id) && !result.ok) {
|
||||
failed = true;
|
||||
}
|
||||
if (required.has(result.id) && !result.ok) failed = true;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
|
||||
@@ -36,8 +36,8 @@ Do not update these from mixed sources. All three ASC fields must come from the
|
||||
## Workflow Shape
|
||||
|
||||
- Public release branch may carry mac-only packaging fixes after the stable tag/npm are already live.
|
||||
- Use `source_ref=release/YYYY.M.PATCH` for private mac preflight/validation when building that branch variation.
|
||||
- Keep `tag=vYYYY.M.PATCH` pointing at the original stable release commit.
|
||||
- Use `source_ref=release/YYYY.M.D` for private mac preflight/validation when building that branch variation.
|
||||
- Keep `tag=vYYYY.M.D` pointing at the original stable release commit.
|
||||
- Real mac publish must reuse:
|
||||
- a successful private mac preflight run for the same tag/source SHA
|
||||
- a successful private mac validation run for the same tag/source SHA
|
||||
@@ -56,37 +56,37 @@ Private preflight:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.PATCH \
|
||||
-f source_ref=release/YYYY.M.PATCH \
|
||||
-f tag=vYYYY.M.D \
|
||||
-f source_ref=release/YYYY.M.D \
|
||||
-f preflight_only=true \
|
||||
-f smoke_test_only=false \
|
||||
-f allow_late_calver_recovery=false \
|
||||
-f public_release_branch=release/YYYY.M.PATCH
|
||||
-f public_release_branch=release/YYYY.M.D
|
||||
```
|
||||
|
||||
Private validation for a branch-variation preflight:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-validate.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.PATCH \
|
||||
-f source_ref=release/YYYY.M.PATCH
|
||||
-f tag=vYYYY.M.D \
|
||||
-f source_ref=release/YYYY.M.D
|
||||
```
|
||||
|
||||
Real publish:
|
||||
|
||||
```bash
|
||||
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases-private --ref main \
|
||||
-f tag=vYYYY.M.PATCH \
|
||||
-f tag=vYYYY.M.D \
|
||||
-f preflight_only=false \
|
||||
-f smoke_test_only=false \
|
||||
-f preflight_run_id=<successful-preflight-run> \
|
||||
-f validate_run_id=<successful-validation-run> \
|
||||
-f allow_late_calver_recovery=false \
|
||||
-f public_release_branch=release/YYYY.M.PATCH
|
||||
-f public_release_branch=release/YYYY.M.D
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
- `gh release view vYYYY.M.PATCH --repo openclaw/openclaw` shows zip, dmg, dSYM zip, not draft, not prerelease.
|
||||
- Public `main` `appcast.xml` points at `OpenClaw-YYYY.M.PATCH.zip`.
|
||||
- `gh release view vYYYY.M.D --repo openclaw/openclaw` shows zip, dmg, dSYM zip, not draft, not prerelease.
|
||||
- Public `main` `appcast.xml` points at `OpenClaw-YYYY.M.D.zip`.
|
||||
- Appcast entry has `sparkle:version`, `sparkle:shortVersionString`, length, and `sparkle:edSignature`.
|
||||
|
||||
@@ -10,19 +10,12 @@ Use this skill for release and publish-time workflow. Load `$release-private` if
|
||||
## Respect release guardrails
|
||||
|
||||
- Do not change version numbers without explicit operator approval.
|
||||
- Versions use `YYYY.M.PATCH`, where `PATCH` is the sequential release-train number within the month, not the calendar day.
|
||||
- Choose a new beta train from stable and beta releases only. Alpha-only tags do not consume or advance the beta/stable patch number. Continue the highest existing unpublished/published beta train with the next `beta.N` when appropriate; otherwise increment the highest stable/beta patch by one and start at `beta.1`.
|
||||
- Example: after stable `2026.6.5`, the next new beta train is `2026.6.6-beta.1`, even if automated alpha-only tags such as `2026.6.10-alpha.1` exist.
|
||||
- Ask permission before any npm publish or release step.
|
||||
- This skill should be sufficient to drive the normal release flow end-to-end.
|
||||
- Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy.
|
||||
- Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself.
|
||||
- Do not edit the root `README.md` as release prep, release closeout, or a
|
||||
substitute for release notes. Package-root README validation is a hard
|
||||
packaging gate, but a release only changes README content when an actual
|
||||
user-facing documentation contract changed.
|
||||
- Normal release work happens on a branch cut from `main`, not directly on
|
||||
`main`. Use `release/YYYY.M.PATCH` for the branch name.
|
||||
`main`. Use `release/YYYY.M.D` for the branch name.
|
||||
- If the operator asks for a release without saying stable/full, default to
|
||||
beta only. Continue from beta to stable only when the operator explicitly asks
|
||||
for the full release or an automated beta-and-stable train.
|
||||
@@ -56,21 +49,17 @@ Use this skill for release and publish-time workflow. Load `$release-private` if
|
||||
the next beta number until the matching npm package has actually published.
|
||||
If a published beta needs a fix, commit the fix on the release branch and
|
||||
increment to the next `-beta.N`.
|
||||
- For a beta release train, keep Full Release Validation as a pre-publish gate
|
||||
unless the operator explicitly waives it. Run the fast local preflight, npm
|
||||
preflight, full release validation, and performance in parallel where safe.
|
||||
If anything fails before npm publish, fix it on the release branch,
|
||||
forward-port the fix to `main`, move the unpublished beta tag/prerelease to
|
||||
the fixed commit, and rerun the affected pre-publish gates. If anything fails
|
||||
after npm publish, fix it, forward-port to `main`, increment beta number, and
|
||||
repeat. After each beta publish, run the published-package roster focused on
|
||||
install/update/Docker/Parallels/NPM Telegram. For later beta attempts, rerun
|
||||
only lanes whose evidence changed unless the fix touches broad release,
|
||||
install/update, plugin, Docker, Parallels, or live QA behavior. After each
|
||||
beta is live, scan current `main` once for critical fixes that landed after
|
||||
the release branch cut and backport only important low-risk fixes. Operators
|
||||
may authorize up to 4 autonomous beta attempts; after 4 failed beta attempts,
|
||||
stop and report.
|
||||
- For a beta release train, run the fast local preflight first, publish the
|
||||
beta to npm `beta`, then run the expensive published-package roster focused
|
||||
on install/update/Docker/Parallels/NPM Telegram. If anything fails, fix it on
|
||||
the release branch, commit/push/pull, increment beta number, and repeat. Run
|
||||
the full expensive roster at least once before stable/latest promotion; for
|
||||
later beta attempts, rerun only lanes whose evidence changed unless the fix
|
||||
touches broad release, install/update, plugin, Docker, Parallels, or live QA
|
||||
behavior. After each beta is published, scan current `main` once for critical
|
||||
fixes that landed after the release branch cut and backport only important
|
||||
low-risk fixes. Operators may authorize up to 4 autonomous beta attempts;
|
||||
after 4 failed beta attempts, stop and report.
|
||||
- As soon as the release candidate SHA exists, dispatch `OpenClaw Performance`
|
||||
with `target_ref=<release-sha>` in parallel with the other release work. Do
|
||||
not wait for full release validation to start the performance signal.
|
||||
@@ -80,51 +69,8 @@ Use this skill for release and publish-time workflow. Load `$release-private` if
|
||||
or clawgrit reports. Report regressions explicitly. A major regression is a
|
||||
release blocker unless the operator waives it or the data clearly proves
|
||||
infrastructure noise.
|
||||
- Heal CI before tagging or publishing. The exact candidate SHA must have green
|
||||
`Full Release Validation`, including the root Dockerfile/install-smoke path.
|
||||
Treat a red Docker, package, or release workflow lane as a release-branch
|
||||
defect until the smallest correct fix is landed and proven; do not waive it
|
||||
because npm preflight or another sibling lane passed.
|
||||
- Keep the canonical `scripts/pr` runner authoritative for prepare and merge
|
||||
artifacts. A release-gate policy change may use focused candidate tests and
|
||||
exact-SHA hosted CI for proof, but never route `prepare-*` or `merge-*`
|
||||
through PR-controlled scripts or synthesize prepare artifacts to bootstrap
|
||||
the change. If the current canonical gate cannot validate the new policy,
|
||||
stop for explicit maintainer direction rather than weakening that boundary.
|
||||
- In maintainer Testbox mode, use `OPENCLAW_TESTBOX=1 scripts/pr prepare-run
|
||||
<PR>` only after the exact PR head has passed `CI` and every scheduled
|
||||
hosted gate. For a workflow change, that means `Blacksmith Testbox`,
|
||||
`Blacksmith ARM Testbox`, `Blacksmith Build Artifacts Testbox`, and
|
||||
`Workflow Sanity`; only gates GitHub actually scheduled for that exact head
|
||||
are required. This preserves the canonical prepare artifacts while avoiding
|
||||
a redundant broad local suite. A
|
||||
literal `CHANGELOG.md`-only head gets a clean diff check instead because
|
||||
those workflows intentionally do not dispatch. Documentation and README
|
||||
changes still require CI. If `merge-run` requires a mainline sync, run
|
||||
`OPENCLAW_TESTBOX=1 scripts/pr prepare-sync-head <PR>`, wait for those hosted
|
||||
gates on the newly pushed SHA, then run `prepare-run` again.
|
||||
- If an exact PR-head CI run has no active jobs because Blacksmith capacity is
|
||||
stalled, a maintainer may dispatch the explicit GitHub-hosted fallback from
|
||||
the PR head branch:
|
||||
`gh workflow run ci.yml --repo openclaw/openclaw --ref <pr-head-branch> -f
|
||||
target_ref=<full-pr-sha> -f include_android=true -f release_gate=true`.
|
||||
Use it only for an observed provider queue stall, never for failed CI or as a
|
||||
routine shortcut. The run must be named `CI release gate <full-pr-sha>` and
|
||||
pass on that exact SHA; the native hosted-gate verifier rejects generic manual
|
||||
CI runs. If `Blacksmith Build Artifacts Testbox` is the only remaining
|
||||
required gate and it is still queued without a runner, the same completed
|
||||
fallback CI may cover it because its `build-artifacts` job builds, packages,
|
||||
and smoke tests those artifacts. The verifier records that coverage. Never
|
||||
use this coverage when the artifact workflow has started, failed, been
|
||||
cancelled, or been skipped. Then rerun `OPENCLAW_TESTBOX=1 scripts/pr
|
||||
prepare-run <PR>`.
|
||||
- Generate the changelog before every beta, beta rerun, stable release, or
|
||||
stable rerun, before version/tag preparation. Use
|
||||
`$openclaw-changelog-update` for the rewrite. Do not continue release prep if
|
||||
the target `CHANGELOG.md` section does not have `### Highlights`,
|
||||
`### Changes`, and `### Fixes`, grouped by user-facing surface while
|
||||
preserving every relevant PR/issue ref and every human `Thanks @...`
|
||||
attribution in the grouped bullet.
|
||||
- Generate the changelog before version/tag preparation so the top changelog
|
||||
section is deduped and ordered by user impact.
|
||||
- Do not create beta-specific `CHANGELOG.md` headings. Beta releases use the
|
||||
stable base version section, for example `v2026.4.20-beta.1` uses
|
||||
`## 2026.4.20` release notes.
|
||||
@@ -137,39 +83,11 @@ prepare-run <PR>`.
|
||||
## Keep release channel naming aligned
|
||||
|
||||
- `stable`: tagged releases only, published to npm `beta` by default; operators may target npm `latest` explicitly or promote later
|
||||
- `beta`: prerelease tags like `vYYYY.M.PATCH-beta.N`, with npm dist-tag `beta`
|
||||
- `beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
|
||||
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
|
||||
- `dev`: moving head on `main`
|
||||
- When using a beta Git tag, publish npm with the matching beta version suffix so the plain version is not consumed or blocked
|
||||
|
||||
## Close stable releases on main
|
||||
|
||||
Stable publication is not complete until `main` carries the actual shipped release state.
|
||||
|
||||
1. Start from fresh latest `main`. Audit `release/YYYY.M.PATCH` against it and
|
||||
forward-port real fixes that are absent from `main`. Do not blindly merge
|
||||
release-only compatibility, test, or validation adapters into newer `main`.
|
||||
2. Set `main` to the shipped stable version, not a speculative next train. Run
|
||||
`pnpm release:prep` after the root version change, then
|
||||
`pnpm deps:shrinkwrap:generate`.
|
||||
3. Make `CHANGELOG.md`'s `## YYYY.M.PATCH` section on `main` exactly match the
|
||||
tagged release branch. Include the stable `appcast.xml` update when the mac
|
||||
release published one.
|
||||
4. Do not add `YYYY.M.PATCH+1`, a beta version, or an empty future changelog
|
||||
section to `main` until the operator explicitly starts that release train.
|
||||
5. Run `pnpm release:generated:check`, `pnpm deps:shrinkwrap:check`, and
|
||||
`OPENCLAW_TESTBOX=1 pnpm check:changed`. Push, then verify `origin/main`
|
||||
contains the shipped version and changelog before calling the stable release
|
||||
done.
|
||||
6. Keep repository variables `RELEASE_ROLLBACK_DRILL_ID` and
|
||||
`RELEASE_ROLLBACK_DRILL_DATE` current after each private rollback drill.
|
||||
`openclaw-stable-main-closeout.yml` starts from the `main` push carrying the
|
||||
shipped version, changelog, and appcast after stable publication, then binds
|
||||
immutable evidence to the published tag. Do not declare stable complete
|
||||
until it writes the immutable closeout manifest to the GitHub release. The
|
||||
drill must be within 90 days; manual dispatch is only for repair/replay, and
|
||||
private rollback commands remain in the maintainer-only runbook.
|
||||
|
||||
## Handle versions and release files consistently
|
||||
|
||||
- Version locations include:
|
||||
@@ -181,13 +99,12 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
- `docs/install/updating.md`
|
||||
- Peekaboo Xcode project and plist version fields
|
||||
- Before creating a release tag, make every version location above match the version encoded by that tag.
|
||||
- For fallback correction tags like `vYYYY.M.PATCH-N`, the repo version locations still stay at `YYYY.M.PATCH`.
|
||||
- For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`.
|
||||
- “Bump version everywhere” means all version locations above except `appcast.xml`.
|
||||
- Release signing and notary credentials live outside the repo in the private maintainer docs.
|
||||
- Every stable OpenClaw release ships the npm package, macOS app, and signed
|
||||
Windows Hub installers together. Beta releases normally ship npm/package
|
||||
artifacts first and skip native app build/sign/notarize/promote unless the
|
||||
operator requests native beta validation.
|
||||
- Every stable OpenClaw release ships the npm package and macOS app together.
|
||||
Beta releases normally ship npm/package artifacts first and skip mac app
|
||||
build/sign/notarize unless the operator requests mac beta validation.
|
||||
- Do not let the slower macOS signing/notary path block npm publication once
|
||||
the npm preflight has passed. Keep mac validation/publish running in
|
||||
parallel, publish npm from the successful npm preflight, then start published
|
||||
@@ -202,44 +119,21 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
tagged commit when the delta is mac packaging, signing, workflow, or
|
||||
validation-only release machinery. If mac packaging needs release-branch-only
|
||||
fixes after the stable npm package or GitHub tag is already published, do not
|
||||
create a `vYYYY.M.PATCH-N` correction tag just to change the workflow source.
|
||||
Dispatch the private mac workflows for the original `tag=vYYYY.M.PATCH` with
|
||||
`source_ref=release/YYYY.M.PATCH` and `public_release_branch=release/YYYY.M.PATCH`;
|
||||
create a `vYYYY.M.D-N` correction tag just to change the workflow source.
|
||||
Dispatch the private mac workflows for the original `tag=vYYYY.M.D` with
|
||||
`source_ref=release/YYYY.M.D` and `public_release_branch=release/YYYY.M.D`;
|
||||
provenance checks must prove the source SHA descends from the tag and
|
||||
validation/preflight use the same source. Reserve `vYYYY.M.PATCH-N` correction
|
||||
validation/preflight use the same source. Reserve `vYYYY.M.D-N` correction
|
||||
tags for emergency hotfixes that must publish a new npm package/release
|
||||
identity, not for ordinary mac-only packaging recovery.
|
||||
- The production Sparkle feed lives at `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`, and the canonical published file is `appcast.xml` on `main` in the `openclaw` repo.
|
||||
- That shared production Sparkle feed is stable-only. Beta mac releases may
|
||||
upload assets to the GitHub prerelease, but they must not replace the shared
|
||||
`appcast.xml` unless a separate beta feed exists.
|
||||
- For fallback correction tags like `vYYYY.M.PATCH-N`, the repo version still stays
|
||||
at `YYYY.M.PATCH`, but the mac release must use a strictly higher numeric
|
||||
- For fallback correction tags like `vYYYY.M.D-N`, the repo version still stays
|
||||
at `YYYY.M.D`, but the mac release must use a strictly higher numeric
|
||||
`APP_BUILD` / Sparkle build than the original release so existing installs
|
||||
see it as newer.
|
||||
- Stable Windows Hub release closeout requires the signed
|
||||
`OpenClawCompanion-Setup-x64.exe`, `OpenClawCompanion-Setup-arm64.exe`, and
|
||||
`OpenClawCompanion-SHA256SUMS.txt` assets on the canonical
|
||||
`openclaw/openclaw` GitHub Release. Pass the exact signed
|
||||
`openclaw/openclaw-windows-node` release tag as `windows_node_tag` to
|
||||
`OpenClaw Release Publish`, together with the candidate-approved
|
||||
`windows_node_installer_digests` map; it prevalidates the published source
|
||||
release and required installers against that map before any publish child,
|
||||
dispatches the public `Windows Node Release` workflow while the OpenClaw
|
||||
release is still a draft, carries those pinned source asset digests
|
||||
unchanged, verifies the expected OpenClaw Foundation Authenticode signer on
|
||||
Windows, re-downloads and checksum-verifies the promoted asset contract, and
|
||||
blocks publication until the canonical asset contract is present. Use direct
|
||||
`Windows Node Release` dispatch only for recovery, always with an exact tag,
|
||||
never `latest`, and the explicit `expected_installer_digests` JSON map from
|
||||
the approved source release. Recovery rejects unexpected
|
||||
`OpenClawCompanion-*` target asset names, then replaces the expected contract
|
||||
assets with the pinned source bytes.
|
||||
- Website Windows Hub download links should target exact canonical
|
||||
`openclaw/openclaw/releases/download/vYYYY.M.PATCH/...` assets for the current
|
||||
stable release, or `releases/latest/download/...` only after verifying the
|
||||
redirect resolves to that same tag, so the installable signed Windows artifact
|
||||
is visible from both the GitHub release page and openclaw.ai.
|
||||
|
||||
## Build changelog-backed release notes
|
||||
|
||||
@@ -249,9 +143,6 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
section from history, not existing notes. Use the last reachable stable or
|
||||
beta release tag as the base, then inspect every commit through the target
|
||||
release SHA.
|
||||
- The changelog rewrite is not optional for beta reruns: any `beta.N` after a
|
||||
rebase or backport must refresh the same stable-base `## YYYY.M.PATCH` section
|
||||
before the new version/tag commit.
|
||||
- Include both merged PR commits and direct commits on `main`. Direct commits
|
||||
matter: infer notes from their subject, body, touched files, linked issues,
|
||||
tests, and nearby code when no PR body exists.
|
||||
@@ -265,28 +156,11 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
- Add missed user-facing changes, remove internal-only noise, dedupe overlapping
|
||||
PR/direct-commit entries, and sort each section from most to least interesting
|
||||
for users.
|
||||
- Group related highlights, changes, and fixes by user-facing surface and
|
||||
impact, but never lose traceability: each grouped bullet keeps every relevant
|
||||
`#issue`, `(#PR)`, `Fixes #...`, and every human `Thanks @...` handle.
|
||||
Multiple thanks in one bullet are expected when multiple contributor PRs are
|
||||
grouped.
|
||||
- Changelog entries should be user-facing, not internal release-process notes.
|
||||
- GitHub release and prerelease bodies must use the full matching
|
||||
`CHANGELOG.md` version section, not highlights or an excerpt. When creating
|
||||
or editing a release, extract from `## YYYY.M.PATCH` through the line before the
|
||||
or editing a release, extract from `## YYYY.M.D` through the line before the
|
||||
next level-2 heading and use that complete block as the release notes.
|
||||
- Before publishing or closing a release, run
|
||||
`$openclaw-changelog-update`'s `verify-release-notes.mjs` with every stable
|
||||
and beta release tag in the train. Do not publish or leave a page live when
|
||||
it is missing a source-history reference, eligible human credit, or the
|
||||
complete matching changelog body.
|
||||
- To update an existing GitHub Release body, resolve the numeric release id and
|
||||
patch that resource with the notes file as the `body` field:
|
||||
`gh api repos/openclaw/openclaw/releases/tags/vYYYY.M.PATCH --jq .id`, then
|
||||
`gh api -X PATCH repos/openclaw/openclaw/releases/<id> -F body=@/tmp/notes.md`.
|
||||
Do not trust `gh release edit --notes-file` or `--input` JSON if verification
|
||||
disagrees; verify with `gh api repos/openclaw/openclaw/releases/<id>` because
|
||||
the tag lookup and `gh release view` can lag or show stale body text.
|
||||
- When preparing release notes, scan `src/plugins/compat/registry.ts` and
|
||||
`src/commands/doctor/shared/deprecation-compat.ts` for compatibility records
|
||||
with `warningStarts` or `removeAfter` within 7 days after the release date.
|
||||
@@ -295,10 +169,10 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
record's `docsPath` or `/plugins/compatibility` when no more specific
|
||||
deprecation page exists.
|
||||
- When cutting a mac release with a beta GitHub prerelease:
|
||||
- tag `vYYYY.M.PATCH-beta.N` from the release commit
|
||||
- create a prerelease titled `openclaw YYYY.M.PATCH-beta.N`
|
||||
- tag `vYYYY.M.D-beta.N` from the release commit
|
||||
- create a prerelease titled `openclaw YYYY.M.D-beta.N`
|
||||
- use release notes from the stable base `CHANGELOG.md` version section
|
||||
(`## YYYY.M.PATCH`), not a beta-specific heading
|
||||
(`## YYYY.M.D`), not a beta-specific heading
|
||||
- attach at least the zip and dSYM zip, plus dmg if available
|
||||
- Keep the top version entries in `CHANGELOG.md` sorted by impact:
|
||||
- `### Changes` first
|
||||
@@ -308,10 +182,10 @@ Stable publication is not complete until `main` carries the actual shipped relea
|
||||
|
||||
Use the OpenClaw account's existing release-post style:
|
||||
|
||||
- Format: `OpenClaw YYYY.M.PATCH 🦞` or `🦞 OpenClaw YYYY.M.PATCH is live`, blank line,
|
||||
- Format: `OpenClaw YYYY.M.D 🦞` or `🦞 OpenClaw YYYY.M.D is live`, blank line,
|
||||
then 3-4 emoji-led bullets, blank line, one short punchline, then the release
|
||||
link.
|
||||
- For beta: say `OpenClaw YYYY.M.PATCH-beta.N 🦞` or `OpenClaw YYYY.M.PATCH beta N is
|
||||
- For beta: say `OpenClaw YYYY.M.D-beta.N 🦞` or `OpenClaw YYYY.M.D beta N is
|
||||
live`; keep it clearly beta and avoid implying stable promotion.
|
||||
- Lead with user-visible capabilities, then important integrations, then
|
||||
reliability/security/install fixes. Compress "lots of fixes" into one
|
||||
@@ -396,7 +270,6 @@ Upgrade with the beta channel.
|
||||
Before tagging or publishing, run:
|
||||
|
||||
```bash
|
||||
pnpm release:fast-pretag-check
|
||||
pnpm check:architecture
|
||||
pnpm build
|
||||
pnpm ui:build
|
||||
@@ -405,38 +278,6 @@ pnpm release:check
|
||||
pnpm test:install:smoke
|
||||
```
|
||||
|
||||
- Treat `pnpm release:fast-pretag-check` as a hard packaging gate. Every
|
||||
publishable plugin must have a non-empty package-root `README.md`, build its
|
||||
package-local runtime, and pass the npm and ClawHub release metadata checks
|
||||
before a tag or publish workflow can start. Do not defer README, entrypoint,
|
||||
or packed-artifact failures to postpublish verification.
|
||||
- Before tagging, require green CI for the exact release-candidate SHA, not an
|
||||
earlier branch SHA. Heal every related red CI, release-check, packaging, or
|
||||
root-Dockerfile lane on the release branch, forward-port the fix to `main`,
|
||||
and rerun the affected exact-SHA gates. Never waive a red Docker lane because
|
||||
npm preflight passed.
|
||||
- Root Dockerfile proof is mandatory before every beta and stable tag. Run the
|
||||
release `install-smoke` group or equivalent root Dockerfile build for the
|
||||
exact candidate SHA and require it to pass. The tag-triggered Docker Release
|
||||
workflow is post-tag publishing, not the first valid proof that the root
|
||||
Dockerfile can build.
|
||||
- Before tagging, diff publishable plugin package manifests against the last
|
||||
reachable stable/beta release tag. For every newly publishable package
|
||||
(`openclaw.release.publishToNpm: true` or `publishToClawHub: true`) whose
|
||||
package name did not exist in the base tag, verify the target registry package
|
||||
already exists in npm/ClawHub or stop and help the owner mint/prepublish the
|
||||
package first. Do not hide or disable release surfaces just to unblock a
|
||||
train unless the owner explicitly decides the plugin should not ship in that
|
||||
release; first-package registry ownership is release prep, not product
|
||||
rollback. The mint/prepublish path must either be the real release publish
|
||||
path for the auto-bumped beta version, or a deliberately non-consuming
|
||||
registry-prep step that cannot occupy the next beta version/tag. Confirm
|
||||
registry owner, npm scope/package-creation permission, provenance path, and
|
||||
first-package publish plan before the full release publish continues. Useful
|
||||
npm probe:
|
||||
`npm view <package-name> version dist-tags --json --prefer-online`; a 404 for
|
||||
a package newly added to the release is a release-prep blocker, not something
|
||||
to discover from the publish job.
|
||||
- Use `pnpm qa:otel:smoke` when release validation needs telemetry coverage.
|
||||
It starts a local OTLP/HTTP trace receiver, runs QA-lab's
|
||||
`otel-trace-smoke`, and checks span names plus content/identifier redaction
|
||||
@@ -455,8 +296,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
```
|
||||
|
||||
- This verifies the published registry install path in a fresh temp prefix.
|
||||
- For stable correction releases like `YYYY.M.PATCH-N`, it also verifies the
|
||||
upgrade path from `YYYY.M.PATCH` to `YYYY.M.PATCH-N` so a correction publish cannot
|
||||
- For stable correction releases like `YYYY.M.D-N`, it also verifies the
|
||||
upgrade path from `YYYY.M.D` to `YYYY.M.D-N` so a correction publish cannot
|
||||
silently leave existing global installs on the old base stable payload.
|
||||
- Treat install smoke as a pack-budget gate too. `pnpm test:install:smoke`
|
||||
now fails the candidate update tarball when npm reports an oversized
|
||||
@@ -603,7 +444,7 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
`npm login --auth-type=legacy`, then confirm `npm whoami` reports
|
||||
`steipete`.
|
||||
- Promote with a fresh OTP:
|
||||
`npm dist-tag add openclaw@YYYY.M.PATCH latest --otp "$OTP"`.
|
||||
`npm dist-tag add openclaw@YYYY.M.D latest --otp "$OTP"`.
|
||||
- Verify with a cache-bypassed registry read, for example:
|
||||
`npm view openclaw dist-tags --json --prefer-online --cache /tmp/openclaw-npm-cache-verify-$$`
|
||||
and `npm view openclaw@latest version dist.tarball --json --prefer-online`.
|
||||
@@ -614,10 +455,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
- The npm workflow and the private mac publish workflow accept
|
||||
`preflight_only=true` to run validation/build/package steps without uploading
|
||||
public release assets.
|
||||
- Real npm publish requires a prior successful npm preflight run id and the
|
||||
successful Full Release Validation run id for the same tag/SHA so the publish
|
||||
job promotes the prepared tarball instead of rebuilding it and attaches the
|
||||
correct release evidence.
|
||||
- Real npm publish requires a prior successful npm preflight run id so the
|
||||
publish job promotes the prepared tarball instead of rebuilding it.
|
||||
- Real private mac publish requires a prior successful private mac preflight
|
||||
run id so the publish job promotes the prepared artifacts instead of
|
||||
rebuilding or renotarizing them again.
|
||||
@@ -627,19 +466,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
- `preflight_only=true` on the npm workflow is also the right way to validate an
|
||||
existing tag after publish; it should keep running the build checks even when
|
||||
the npm version is already published.
|
||||
- npm registry metadata is eventually consistent immediately after trusted
|
||||
publishing. Keep postpublish `npm view` checks on bounded `--prefer-online`
|
||||
retries, and carry that verified tarball/integrity metadata into later proof
|
||||
steps instead of reading the registry again. If the OpenClaw npm child
|
||||
succeeded but the parent publish workflow failed on an immediate exact-version
|
||||
`E404`, verify the exact version with a cache-bypassed registry read, run the
|
||||
standalone postpublish verifier and the full beta verifier with the original
|
||||
successful child run IDs, then finalize the draft, dependency evidence asset,
|
||||
and release proof manually. Never rerun the publish workflow for that
|
||||
already-published version.
|
||||
- npm validation-only preflight may still be dispatched from ordinary branches
|
||||
when testing workflow changes before merge. Release checks and real publish
|
||||
use only `main` or `release/YYYY.M.PATCH`.
|
||||
use only `main` or `release/YYYY.M.D`.
|
||||
- `.github/workflows/macos-release.yml` in `openclaw/openclaw` is now a
|
||||
public validation-only handoff. It validates the tag/release state and points
|
||||
operators to the private repo. It still rebuilds the JS outputs needed for
|
||||
@@ -657,14 +486,13 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
instead of uploading public GitHub release assets.
|
||||
- Private smoke-test runs upload ad-hoc, non-notarized build artifacts as
|
||||
workflow artifacts and intentionally skip stable `appcast.xml` generation.
|
||||
- For stable releases, npm preflight, Full Release Validation, public mac
|
||||
validation, private mac validation, and private mac preflight must all pass
|
||||
before any real publish run starts. For beta releases, npm preflight and Full
|
||||
Release Validation must pass before npm publish unless the operator explicitly
|
||||
waives the full gate; mac beta validation is still only required when
|
||||
requested.
|
||||
- For stable releases, npm preflight, public mac validation, private mac
|
||||
validation, and private mac preflight must all pass before any real publish
|
||||
run starts. For beta releases, npm preflight plus the selected Docker,
|
||||
install/update, Parallels, and release-check lanes are sufficient unless mac
|
||||
beta validation was explicitly requested.
|
||||
- Real publish runs may be dispatched from `main` or from a
|
||||
`release/YYYY.M.PATCH` branch. For release-branch runs, the tag must be contained
|
||||
`release/YYYY.M.D` branch. For release-branch runs, the tag must be contained
|
||||
in that release branch, and the real publish must reuse a successful preflight
|
||||
from the same branch.
|
||||
- The release workflows stay tag-based; rely on the documented release sequence
|
||||
@@ -692,11 +520,7 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
- Use `NPM_TOKEN` only for explicit npm dist-tag management modes, because npm
|
||||
does not support trusted publishing for `npm dist-tag add`.
|
||||
- `@openclaw/*` plugin publishes use a separate maintainer-only flow.
|
||||
- Publishable plugins that are new to npm require owner-led first-package
|
||||
minting before the full release publish. Do not consume the next beta version
|
||||
with an ad-hoc manual package publish; use the release-owned auto-bumped
|
||||
version path, or a non-consuming registry setup/preflight step. Bundled
|
||||
disk-tree-only plugins stay unpublished.
|
||||
- Only publish plugins that already exist on npm; bundled disk-tree-only plugins stay unpublished.
|
||||
|
||||
## Fallback local mac publish
|
||||
|
||||
@@ -736,8 +560,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
4. Pull latest `main` and confirm current `main` CI is green.
|
||||
5. Run `/changelog` for the stable base target version on `main`, commit the
|
||||
changelog rewrite immediately, push, and pull/rebase. For beta releases,
|
||||
keep the changelog heading as `## YYYY.M.PATCH`, not `## YYYY.M.PATCH-beta.N`.
|
||||
6. Create `release/YYYY.M.PATCH` from that post-changelog `main` commit.
|
||||
keep the changelog heading as `## YYYY.M.D`, not `## YYYY.M.D-beta.N`.
|
||||
6. Create `release/YYYY.M.D` from that post-changelog `main` commit.
|
||||
7. Make every repo version location match the beta tag before creating it.
|
||||
8. Commit release preparation changes on the release branch and push the branch.
|
||||
9. Immediately dispatch Actions > `OpenClaw Performance` from `main` with
|
||||
@@ -745,18 +569,15 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
off, live OpenAI off, and regression failure off. Let it run in parallel
|
||||
with preflight and validation work.
|
||||
10. Run the fast local beta preflight from the release branch before any npm
|
||||
preflight or publish. Require exact-SHA CI and root Dockerfile install-smoke
|
||||
to be green before tagging. Keep the remaining expensive Docker, Parallels,
|
||||
and published-package install/update lanes for after the beta is live unless
|
||||
the operator asks to run them before beta publication.
|
||||
preflight or publish. Keep expensive Docker, Parallels, and published-package
|
||||
install/update lanes for after the beta is live unless the operator asks to
|
||||
run them before beta publication.
|
||||
11. For beta releases, skip mac app build/sign/notarize unless beta scope or a
|
||||
release blocker specifically requires it. For stable releases, include the
|
||||
mac app, signing, notarization, and appcast path.
|
||||
12. Confirm the target npm version is not already published.
|
||||
13. Create and push the git tag from the release branch.
|
||||
14. Do not create or publish the matching GitHub release page yet. The real
|
||||
publish workflow creates or undrafts it only after postpublish verification
|
||||
and release evidence upload pass.
|
||||
14. Create or refresh the matching GitHub release.
|
||||
15. Dispatch Actions > `QA-Lab - All Lanes` against the release tag and wait
|
||||
for the mock parity, live Matrix, and live Telegram credentialed-channel
|
||||
lanes to pass.
|
||||
@@ -779,39 +600,21 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
with `preflight_only=true` and wait for it to pass. Save that run id because
|
||||
the real publish requires it to reuse the notarized mac artifacts.
|
||||
21. If any preflight or validation run fails, fix the issue on a new commit,
|
||||
delete the tag and any accidental draft/incomplete GitHub release, recreate
|
||||
the tag from the fixed commit, and rerun all relevant preflights from
|
||||
scratch before continuing. Never reuse old preflight results after the
|
||||
commit changes. Once the npm version exists, do not rerun the publish
|
||||
workflow for that same version; finalize the existing draft/evidence state
|
||||
manually or cut a correction tag. For pushed or published beta tags, do not
|
||||
delete/recreate; increment to the next beta tag. For preflight-only failures
|
||||
where npm did not publish the beta version, delete/recreate the same beta
|
||||
tag and any accidental draft/incomplete prerelease at the fixed commit
|
||||
instead of skipping a prerelease number.
|
||||
22. Start `.github/workflows/openclaw-release-publish.yml` from the same branch with
|
||||
delete the tag and matching GitHub release, recreate them from the fixed
|
||||
commit, and rerun all relevant preflights from scratch before continuing.
|
||||
Never reuse old preflight results after the commit changes. For pushed or
|
||||
published beta tags, do not delete/recreate; increment to the next beta tag.
|
||||
For preflight-only failures where npm did not publish the beta version,
|
||||
delete/recreate the same beta tag and prerelease at the fixed commit instead
|
||||
of skipping a prerelease number.
|
||||
22. Start `.github/workflows/openclaw-npm-release.yml` from the same branch with
|
||||
the same tag for the real publish, choose `npm_dist_tag` (`beta` default,
|
||||
`latest` only when you intentionally want direct stable publish), keep it
|
||||
the same as the preflight run, and pass the successful npm
|
||||
`preflight_run_id` plus the successful `full_release_validation_run_id`.
|
||||
For stable publish, also pass the exact non-prerelease
|
||||
`openclaw/openclaw-windows-node` tag as `windows_node_tag` and its
|
||||
candidate-approved installer digest map as `windows_node_installer_digests`.
|
||||
`preflight_run_id`.
|
||||
23. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`.
|
||||
24. Wait for the real publish workflow to run postpublish verification,
|
||||
create or update the GitHub release as a draft, upload dependency evidence,
|
||||
promote and verify the required Windows Hub assets for stable releases,
|
||||
append release verification proof, and only then undraft/publish it. If a
|
||||
waited plugin publish or Windows Hub promotion fails after OpenClaw npm
|
||||
succeeds, the workflow keeps the release draft with OpenClaw npm evidence
|
||||
and exits red; do not undraft until the gap is repaired. The standalone
|
||||
verifier command remains the first recovery probe:
|
||||
24. Run postpublish verification:
|
||||
`node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>`.
|
||||
For a failed postpublish parent after successful publish children, also run
|
||||
`pnpm release:verify-beta -- <published-version> ... --skip-github-release`
|
||||
with the original child run IDs and an evidence output path before manually
|
||||
recreating the workflow's draft, dependency evidence asset, proof section,
|
||||
and publish step.
|
||||
25. Run the post-published beta verification roster. First scan current `main`
|
||||
for critical fixes that landed after the release branch cut; backport only
|
||||
important low-risk fixes before starting expensive lanes, or increment to
|
||||
@@ -848,13 +651,13 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
|
||||
and `.dSYM.zip` artifacts to the existing GitHub release in
|
||||
`openclaw/openclaw`.
|
||||
32. For stable releases, download `macos-appcast-<tag>` from the successful
|
||||
private mac run, update `appcast.xml` on `main`, verify the feed, then
|
||||
complete the **Close stable releases on main** gate.
|
||||
private mac run, update `appcast.xml` on `main`, and verify the feed. Merge
|
||||
or cherry-pick release branch changes back to `main` after stable succeeds.
|
||||
33. For beta releases, publish the mac assets only when intentionally requested;
|
||||
expect no shared production
|
||||
`appcast.xml` artifact and do not update the shared production feed unless a
|
||||
separate beta feed exists.
|
||||
34. After stable main closeout, verify npm and the attached release artifacts.
|
||||
34. After publish, verify npm and the attached release artifacts.
|
||||
|
||||
## GHSA advisory work
|
||||
|
||||
|
||||
@@ -37,11 +37,9 @@ This is good for auditability if commits are clearly machine-authored and gated
|
||||
- Branch name: `tideclaw/alpha/YYYY-MM-DD-HHMMZ`
|
||||
- Base: current `origin/main` SHA at trigger time.
|
||||
- State file: resolve from `$release-private` on the Tideclaw host.
|
||||
- Release tag: `vYYYY.M.PATCH-alpha.N`
|
||||
- Release tag: `vYYYY.M.D-alpha.N`
|
||||
- npm dist-tag: `alpha`
|
||||
|
||||
`PATCH` is a sequential monthly release-train number, never the calendar day. Determine the alpha train from stable and beta releases; ignore alpha-only patch numbers when choosing the next train. Use one greater than the highest stable/beta patch for the month, then increment only `alpha.N` for repeated nightlies on that train. If a beta exists on that next patch, move alpha to the following train. Legacy alpha-only tags with inflated patch numbers do not advance beta/stable numbering.
|
||||
|
||||
Do not reuse old alpha branches for a new run. If rerunning the same base SHA, create a new timestamped branch and record why.
|
||||
|
||||
## Start
|
||||
@@ -100,7 +98,7 @@ Tideclaw may run beta releases from `#releases` or mentioned `#maintainers` comm
|
||||
Accepted shapes:
|
||||
|
||||
```text
|
||||
@Tideclaw beta release from vYYYY.M.PATCH-alpha.N
|
||||
@Tideclaw beta release from vYYYY.M.D-alpha.N
|
||||
@Tideclaw beta release from tideclaw/alpha/YYYY-MM-DD-HHMMZ
|
||||
@Tideclaw beta release from latest proven alpha
|
||||
```
|
||||
@@ -112,7 +110,7 @@ Rules:
|
||||
3. Verify the source alpha first: GitHub release, npm `alpha` package, release CI, recorded state file, and branch/tag SHA.
|
||||
4. Create a fresh beta branch `tideclaw/beta/YYYY-MM-DD-HHMMZ` from the proven alpha source, not directly from a moving `main`.
|
||||
5. Reuse/squash only stabilization fixes already proven on alpha. Do not import unrelated alpha release mechanics unless the beta release docs require them.
|
||||
6. Compute beta as `vYYYY.M.PATCH-beta.N`, matching npm `--tag beta`. Ignore alpha-only patch numbers when selecting the beta train.
|
||||
6. Compute beta as `vYYYY.M.D-beta.N`, matching npm `--tag beta`.
|
||||
7. Run beta release validation/preflight/full release CI and fix failures on the beta branch.
|
||||
8. Publish beta only after green beta gates. Use GitHub Actions/OIDC, never direct npm publish from the host.
|
||||
9. Final Discord summary must include source alpha, beta tag/version, branch, fix commits, workflow run IDs, npm/GitHub proof, and any skipped/blocked reason.
|
||||
@@ -167,7 +165,7 @@ git push -u origin "$BRANCH"
|
||||
|
||||
After local proof:
|
||||
|
||||
1. Compute the next `vYYYY.M.PATCH-alpha.N` from existing git tags, npm versions, and GitHub releases. Select `PATCH` from stable/beta trains, not the date or the highest alpha-only patch. Reuse the same alpha train and increment `alpha.N` until that patch has a beta; after a beta exists, use the following patch for new alpha builds.
|
||||
1. Compute the next `vYYYY.M.D-alpha.N` from existing git tags, npm versions, and GitHub releases.
|
||||
2. Make the alpha branch package version and release metadata match that tag, commit it, and push the branch.
|
||||
3. Run release validation from the alpha branch, using GitHub CLI, not browser/fetch tools. On the Tideclaw host, bare `gh` is a read-only Codex sandbox wrapper; use `/usr/local/bin/gh-tideclaw-write` for write-capable commands such as `workflow run`, `run cancel`, and publish dispatch:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: security-triage
|
||||
description: "Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof."
|
||||
description: Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof.
|
||||
---
|
||||
|
||||
# Security Triage
|
||||
@@ -87,19 +87,11 @@ When preparing a maintainer-ready close reply:
|
||||
- exact reason for close
|
||||
- exact code refs
|
||||
- exact shipped tag / release facts
|
||||
- fix provenance or canonical duplicate GHSA when applicable
|
||||
- exact fix commit or canonical duplicate GHSA when applicable
|
||||
- optional hardening note only if worthwhile and functionality-preserving
|
||||
|
||||
Keep tone firm, specific, non-defensive.
|
||||
|
||||
## Public Wording Hygiene
|
||||
|
||||
- Keep raw commit hashes, PR titles/numbers, and fix-mechanism summaries out of public advisory text. Use the patched release/version field only.
|
||||
- Keep exact commit SHAs, PRs, and implementation notes in internal notes and verification files.
|
||||
- For hardening/no-publish outcomes, do not add exploit-heavy details, "Fixed by" text, or a "Fix Commit(s)" section. Thank reporters, preserve credit, state the `SECURITY.md` boundary, and say clearly that the GHSA will close without publication.
|
||||
- For published CVE/GHSA text, prefer `### Patched Versions` with the fixed release. Do not explain how the patch works unless Peter explicitly asks for that public detail.
|
||||
- Keep GHSA ids out of changelog and release-note wording unless Peter explicitly asks.
|
||||
|
||||
## Discussion Mode
|
||||
|
||||
When Peter is manually posting GHSA comments, use this flow:
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
name: verify-release
|
||||
description: "Verify an OpenClaw release is fully published across GitHub, npm, plugins, ClawHub, package smoke, and live Gateway agent turns."
|
||||
---
|
||||
|
||||
# Verify Release
|
||||
|
||||
Use this when asked whether an OpenClaw release is fully released, published,
|
||||
promoted, smoke-tested, or live-verified. This is a verification skill, not a
|
||||
publish skill; use `$release-openclaw-maintainer` before changing release state.
|
||||
|
||||
## Rules
|
||||
|
||||
- Resolve short suffixes like `.27` to the concrete CalVer version from the
|
||||
current date/context, then say the resolved version.
|
||||
- Verify live state. Do not trust local checkout state, release notes, or old
|
||||
memory as current truth.
|
||||
- If the checkout is dirty or divergent, use it only for scripts/reference.
|
||||
For version metadata, fetch from GitHub release/tag or unpack the tag tarball
|
||||
under `/tmp`.
|
||||
- Never print secrets. Use inherited live keys only for scoped smoke commands.
|
||||
- Keep the final terse: `yes/no`, evidence bullets, caveats, cleanup.
|
||||
|
||||
## Core Checks
|
||||
|
||||
1. GitHub release:
|
||||
- `gh release view v<VERSION> --repo openclaw/openclaw --json tagName,name,publishedAt,isDraft,isPrerelease,targetCommitish,url,body,assets`
|
||||
- Confirm stable releases are not draft/prerelease.
|
||||
- Confirm release body has npm, CI, plugin npm, ClawHub, mac/appcast evidence
|
||||
links when expected.
|
||||
- Confirm assets expected for stable mac releases are uploaded: zip, dmg,
|
||||
dSYM, dependency evidence, immutable full-validation manifest,
|
||||
postpublish evidence, and stable-main closeout manifest.
|
||||
- Download each immutable evidence asset and its `.sha256` companion, then
|
||||
verify the checksum before trusting the release record.
|
||||
2. Root npm:
|
||||
- `npm view openclaw@<VERSION> version dist-tags.latest dist.tarball dist.integrity time.<VERSION> --json`
|
||||
- `latest` must equal `<VERSION>` for stable.
|
||||
- Record tarball, integrity, publish time.
|
||||
- Confirm the release postpublish evidence records
|
||||
`npmRegistrySignaturesVerified: true` and
|
||||
`npmProvenanceAttestationMatched: true`.
|
||||
3. Plugin publish set:
|
||||
- Get exact tag metadata from GitHub, not the local checkout when dirty:
|
||||
download `https://api.github.com/repos/openclaw/openclaw/tarball/v<VERSION>`
|
||||
into `/tmp/openclaw-v<VERSION>-src`.
|
||||
- Count `extensions/*/package.json` with
|
||||
`openclaw.release.publishToNpm === true` and
|
||||
`openclaw.release.publishToClawHub === true`.
|
||||
- Compare expected counts to workflow job counts:
|
||||
`gh api repos/openclaw/openclaw/actions/runs/<RUN>/jobs --paginate`.
|
||||
- Each expected npm plugin must have version `<VERSION>` and
|
||||
`dist-tags.latest === <VERSION>`.
|
||||
4. ClawHub:
|
||||
- Check the Plugin ClawHub Release workflow conclusion and publish job count.
|
||||
- Use OpenClaw itself for live registry proof:
|
||||
`openclaw plugins search <known-plugin> --json`.
|
||||
- Install one official plugin from ClawHub in an isolated HOME:
|
||||
`openclaw plugins install clawhub:@openclaw/matrix --pin`.
|
||||
Prefer `matrix` unless that plugin is not in the expected set.
|
||||
5. Release workflows:
|
||||
- Verify conclusions for release notes evidence links:
|
||||
Full Release Validation, OpenClaw Release Checks, OpenClaw NPM Release,
|
||||
Plugin NPM Release, Plugin ClawHub Release, mac preflight/validation/publish
|
||||
when stable mac assets are expected.
|
||||
- For stable, verify `OpenClaw Stable Main Closeout` succeeded and its
|
||||
manifest records the matching release tag, current rollback drill, stable
|
||||
soak, and blocking performance evidence.
|
||||
- Summarize only relevant successful/failed jobs; ignore routine skipped
|
||||
optional lanes unless the release body promised them.
|
||||
6. Published package smoke:
|
||||
- In `/tmp`, isolated HOME:
|
||||
`npm exec --yes --package openclaw@<VERSION> -- openclaw --version`.
|
||||
- Run at least one harmless command that touches the published CLI surface,
|
||||
for example `plugins --help` or `gateway --help`.
|
||||
7. Dev Gateway live model smoke:
|
||||
- Use temp HOME/workspace, not the user's normal state:
|
||||
`HOME=/tmp/openclaw-release-smoke/home OPENCLAW_WORKSPACE=/tmp/openclaw-release-smoke/work pnpm openclaw --dev gateway run --auth none --force --verbose`.
|
||||
- Health check via CLI: `openclaw --dev gateway health --json`.
|
||||
- Run one Gateway-backed agent turn with inherited `OPENAI_API_KEY`, short
|
||||
prompt, explicit session key, JSON output, and a known-available model.
|
||||
- If the configured default model fails as unavailable, record that caveat
|
||||
and retry with the newest known-good OpenAI model instead of declaring the
|
||||
release failed.
|
||||
- Stop the gateway and verify the port is not listening.
|
||||
|
||||
## Caveats To Report
|
||||
|
||||
- Dist-tag caveat: stable `latest` is release truth; if optional `beta` mirrors
|
||||
still point at a beta version, report it as a caveat, not a stable-release
|
||||
blocker, unless the user asked to verify beta promotion.
|
||||
- Divergent checkout caveat: say when local source SHA differs from release tag
|
||||
or origin and which live sources were used instead.
|
||||
- Smoke caveat: distinguish Gateway-backed agent success from local embedded
|
||||
fallback. A valid Gateway smoke has health OK plus gateway log/run id for the
|
||||
agent call.
|
||||
111
.crabbox.yaml
111
.crabbox.yaml
@@ -1,21 +1,26 @@
|
||||
profile: openclaw-check
|
||||
# Default OpenClaw runner spend to the Azure-backed Crabbox account.
|
||||
# Use `--provider aws` only for AWS-specific runner proof.
|
||||
provider: azure
|
||||
provider: aws
|
||||
class: standard
|
||||
capacity:
|
||||
market: on-demand
|
||||
market: spot
|
||||
strategy: most-available
|
||||
# The Azure-backed billing account carries the OpenClaw runner credits; use
|
||||
# explicit on-demand capacity instead of low-priority spot, whose regional
|
||||
# quota is too small for broad maintainer proof or parallel Crabbox lanes.
|
||||
fallback: on-demand-after-120s
|
||||
hints: true
|
||||
availabilityZones:
|
||||
- eu-west-1a
|
||||
- eu-west-1b
|
||||
- eu-west-1c
|
||||
regions:
|
||||
- eu-west-1
|
||||
- eu-west-2
|
||||
- eu-central-1
|
||||
- us-east-1
|
||||
- us-west-2
|
||||
actions:
|
||||
workflow: .github/workflows/crabbox-hydrate.yml
|
||||
# Default AWS hydration uses local Actions replay. Use
|
||||
# `crabbox actions hydrate --github-runner --job hydrate-github` when the
|
||||
# hydrate job needs GitHub secrets, or `--github-runner --job
|
||||
# hydrate-windows-daemon` for focused native Windows daemon proof.
|
||||
# hydrate job needs GitHub secrets.
|
||||
job: hydrate
|
||||
ref: main
|
||||
runnerLabels:
|
||||
@@ -23,35 +28,9 @@ actions:
|
||||
- openclaw
|
||||
runnerVersion: latest
|
||||
ephemeral: true
|
||||
blacksmith:
|
||||
org: openclaw
|
||||
workflow: .github/workflows/ci-check-testbox.yml
|
||||
job: check
|
||||
ref: main
|
||||
cache:
|
||||
pnpm: true
|
||||
npm: true
|
||||
git: true
|
||||
volumes:
|
||||
- name: pnpm
|
||||
key: openclaw-linux-node24-pnpm
|
||||
path: /var/cache/crabbox/pnpm
|
||||
sizeGB: 80
|
||||
required: false
|
||||
- name: npm
|
||||
key: openclaw-linux-node24-npm
|
||||
path: /var/cache/crabbox/npm
|
||||
sizeGB: 40
|
||||
required: false
|
||||
aws:
|
||||
# AWS-specific overrides still pin direct `--provider aws` runs without
|
||||
# leaking AWS region names into the Azure default capacity fallback list.
|
||||
region: eu-west-1
|
||||
rootGB: 400
|
||||
azure:
|
||||
# The OpenClaw Azure subscription is reliable in eastus2; eastus rejects the
|
||||
# same SKUs and can stall provisioning.
|
||||
location: eastus2
|
||||
sync:
|
||||
delete: true
|
||||
checksum: false
|
||||
@@ -71,64 +50,4 @@ env:
|
||||
- OPENCLAW_*
|
||||
ssh:
|
||||
user: crabbox
|
||||
# Azure coordinator leases expose SSH on 22. The run wrapper can fall back
|
||||
# from 2222, but `crabbox job run` hydrates via the configured port directly.
|
||||
port: "22"
|
||||
jobs:
|
||||
prewarm:
|
||||
provider: azure
|
||||
target: linux
|
||||
class: standard
|
||||
type: Standard_D4ads_v6
|
||||
market: on-demand
|
||||
idleTimeout: 90m
|
||||
hydrate:
|
||||
actions: true
|
||||
waitTimeout: 20m
|
||||
actions:
|
||||
workflow: .github/workflows/crabbox-hydrate.yml
|
||||
job: hydrate
|
||||
ref: main
|
||||
noSync: true
|
||||
shell: true
|
||||
command: "true"
|
||||
stop: never
|
||||
changed:
|
||||
provider: azure
|
||||
target: linux
|
||||
class: standard
|
||||
type: Standard_D4ads_v6
|
||||
market: on-demand
|
||||
idleTimeout: 90m
|
||||
hydrate:
|
||||
actions: true
|
||||
waitTimeout: 20m
|
||||
actions:
|
||||
workflow: .github/workflows/crabbox-hydrate.yml
|
||||
job: hydrate
|
||||
ref: main
|
||||
shell: true
|
||||
command: |
|
||||
set -euo pipefail
|
||||
if ! git status --short >/dev/null 2>&1; then
|
||||
rm -rf .git
|
||||
git init -q
|
||||
git add -A
|
||||
if ! git diff --cached --quiet; then
|
||||
git -c user.name=OpenClaw -c user.email=ci@openclaw.local commit -q --no-gpg-sign -m remote-check-tree
|
||||
fi
|
||||
fi
|
||||
env CI=1 corepack pnpm check --timed
|
||||
stop: always
|
||||
testbox-changed:
|
||||
provider: blacksmith-testbox
|
||||
target: linux
|
||||
idleTimeout: 90m
|
||||
hydrate:
|
||||
actions: false
|
||||
actions:
|
||||
workflow: .github/workflows/ci-check-testbox.yml
|
||||
job: check
|
||||
ref: main
|
||||
command: env OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 CI=1 corepack pnpm check:changed
|
||||
stop: always
|
||||
port: "2222"
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,6 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
CLAUDE.md -text
|
||||
src/gateway/server-methods/CLAUDE.md -text
|
||||
ui/src/i18n/.i18n/* linguist-generated
|
||||
ui/src/i18n/locales/*.ts linguist-generated
|
||||
ui/src/i18n/locales/en.ts -linguist-generated
|
||||
|
||||
13
.github/CODEOWNERS
vendored
13
.github/CODEOWNERS
vendored
@@ -11,15 +11,8 @@
|
||||
/.github/workflows/codeql.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-android-critical-security.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/codeql-critical-quality.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/dependency-guard.yml @openclaw/openclaw-secops
|
||||
/.github/workflows/security-sensitive-guard.yml @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-guard-workflow.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-guard-script.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/security-sensitive-guard-workflow.test.ts @openclaw/openclaw-secops
|
||||
/test/scripts/security-sensitive-guard-script.test.ts @openclaw/openclaw-secops
|
||||
/scripts/github/dependency-guard.mjs @openclaw/openclaw-secops
|
||||
/scripts/github/security-sensitive-guard.mjs @openclaw/openclaw-secops
|
||||
/.gitignore @openclaw/openclaw-secops
|
||||
/.github/workflows/dependency-change-awareness.yml @openclaw/openclaw-secops
|
||||
/test/scripts/dependency-change-awareness-workflow.test.ts @openclaw/openclaw-secops
|
||||
/package-lock.json @openclaw/openclaw-secops
|
||||
/npm-shrinkwrap.json @openclaw/openclaw-secops
|
||||
/extensions/*/package-lock.json @openclaw/openclaw-secops
|
||||
@@ -36,7 +29,7 @@
|
||||
/src/gateway/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/security-path*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/openclaw-secops
|
||||
/packages/gateway-protocol/src/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/protocol/**/*secret*.ts @openclaw/openclaw-secops
|
||||
/src/gateway/server-methods/secrets*.ts @openclaw/openclaw-secops
|
||||
/src/agents/*auth*.ts @openclaw/openclaw-secops
|
||||
/src/agents/**/*auth*.ts @openclaw/openclaw-secops
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,8 +11,6 @@ body:
|
||||
Do not speculate or infer beyond the evidence. If a narrative section cannot be answered from the available evidence, respond with exactly `NOT_ENOUGH_INFO`.
|
||||
|
||||
If this is a plugin beta-release blocker, rename the issue title to `Beta blocker: <plugin-name> - <summary>` and apply the `beta-blocker` label after filing.
|
||||
|
||||
Please only report one issue per submission. Break multiple issues up into separate submissions.
|
||||
- type: dropdown
|
||||
id: bug_type
|
||||
attributes:
|
||||
|
||||
4
.github/actionlint.yaml
vendored
4
.github/actionlint.yaml
vendored
@@ -14,10 +14,6 @@ self-hosted-runner:
|
||||
- blacksmith-16vcpu-ubuntu-2404-arm
|
||||
- blacksmith-6vcpu-macos-latest
|
||||
- blacksmith-12vcpu-macos-latest
|
||||
- blacksmith-6vcpu-macos-15
|
||||
- blacksmith-12vcpu-macos-15
|
||||
- blacksmith-6vcpu-macos-26
|
||||
- blacksmith-12vcpu-macos-26
|
||||
|
||||
# Ignore patterns for known issues
|
||||
paths:
|
||||
|
||||
24
.github/actions/detect-docs-changes/action.yml
vendored
24
.github/actions/detect-docs-changes/action.yml
vendored
@@ -35,29 +35,17 @@ runs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docs_changed=false
|
||||
non_docs=false
|
||||
while IFS= read -r changed_path; do
|
||||
case "$changed_path" in
|
||||
test/fixtures/*)
|
||||
non_docs=true
|
||||
;;
|
||||
docs/* | *.md | *.mdx)
|
||||
docs_changed=true
|
||||
;;
|
||||
*)
|
||||
non_docs=true
|
||||
;;
|
||||
esac
|
||||
done <<< "$CHANGED"
|
||||
|
||||
if [ "$docs_changed" = "true" ]; then
|
||||
# Check if any changed file is a doc
|
||||
DOCS=$(echo "$CHANGED" | grep -E '^docs/|\.md$|\.mdx$' || true)
|
||||
if [ -n "$DOCS" ]; then
|
||||
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
if [ "$non_docs" = "false" ]; then
|
||||
# Check if all changed files are docs or markdown
|
||||
NON_DOCS=$(echo "$CHANGED" | grep -vE '^docs/|\.md$|\.mdx$' || true)
|
||||
if [ -z "$NON_DOCS" ]; then
|
||||
echo "docs_only=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Docs-only change detected — skipping heavy jobs"
|
||||
else
|
||||
|
||||
2
.github/actions/docker-e2e-plan/action.yml
vendored
2
.github/actions/docker-e2e-plan/action.yml
vendored
@@ -113,7 +113,7 @@ runs:
|
||||
|
||||
- name: Download OpenClaw Docker E2E package
|
||||
if: inputs.hydrate-artifacts == 'true' && steps.plan.outputs.needs_package == '1'
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.package-artifact-name }}
|
||||
path: .artifacts/docker-e2e-package
|
||||
|
||||
10
.github/actions/ensure-base-commit/action.yml
vendored
10
.github/actions/ensure-base-commit/action.yml
vendored
@@ -38,15 +38,9 @@ runs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
fetch_base_ref() {
|
||||
timeout --signal=TERM --kill-after=10s 30s git \
|
||||
-c protocol.version=2 \
|
||||
fetch "$@"
|
||||
}
|
||||
|
||||
for deepen_by in 25 100 300; do
|
||||
echo "Base commit missing; deepening $FETCH_REF by $deepen_by."
|
||||
if ! fetch_base_ref --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
|
||||
if ! git fetch --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
|
||||
echo "::warning title=ensure-base-commit fetch failed::Failed to deepen $FETCH_REF by $deepen_by while looking for $BASE_SHA"
|
||||
fi
|
||||
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
|
||||
@@ -56,7 +50,7 @@ runs:
|
||||
done
|
||||
|
||||
echo "Base commit still missing; fetching full history for $FETCH_REF."
|
||||
if ! fetch_base_ref --no-tags origin -- "$FETCH_REF"; then
|
||||
if ! git fetch --no-tags origin -- "$FETCH_REF"; then
|
||||
echo "::warning title=ensure-base-commit fetch failed::Failed to fetch full history for $FETCH_REF while looking for $BASE_SHA"
|
||||
fi
|
||||
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
|
||||
|
||||
17
.github/actions/setup-node-env/action.yml
vendored
17
.github/actions/setup-node-env/action.yml
vendored
@@ -20,13 +20,9 @@ inputs:
|
||||
required: false
|
||||
default: "true"
|
||||
use-actions-cache:
|
||||
description: Whether to restore the pnpm store with actions/cache.
|
||||
description: Whether to restore and save the pnpm store with actions/cache.
|
||||
required: false
|
||||
default: "true"
|
||||
save-actions-cache:
|
||||
description: Whether to save the pnpm store with actions/cache after install when no exact cache restored.
|
||||
required: false
|
||||
default: "false"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -49,7 +45,6 @@ runs:
|
||||
openclaw_ensure_node "$REQUESTED_NODE_VERSION"
|
||||
|
||||
- name: Setup pnpm
|
||||
id: setup-pnpm
|
||||
uses: ./.github/actions/setup-pnpm-store-cache
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
@@ -60,7 +55,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm install -g bun@1.3.14
|
||||
npm install -g bun@1.3.13
|
||||
|
||||
- name: Runtime versions
|
||||
shell: bash
|
||||
@@ -128,7 +123,6 @@ runs:
|
||||
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
|
||||
mkdir -p "$PNPM_CONFIG_MODULES_DIR"
|
||||
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
|
||||
export NODE_PATH="$PNPM_CONFIG_MODULES_DIR${NODE_PATH:+:$NODE_PATH}"
|
||||
fi
|
||||
pnpm "${install_args[@]}" || pnpm "${install_args[@]}"
|
||||
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
|
||||
@@ -136,10 +130,3 @@ runs:
|
||||
ln -sfn "$PNPM_CONFIG_MODULES_DIR" node_modules
|
||||
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
|
||||
fi
|
||||
|
||||
- name: Save pnpm store cache
|
||||
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ steps.setup-pnpm.outputs.store-path }}
|
||||
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}
|
||||
|
||||
@@ -14,7 +14,7 @@ inputs:
|
||||
required: false
|
||||
default: ""
|
||||
use-actions-cache:
|
||||
description: Whether actions/cache should restore the pnpm store.
|
||||
description: Whether actions/cache should cache the pnpm store.
|
||||
required: false
|
||||
default: "true"
|
||||
outputs:
|
||||
@@ -24,15 +24,6 @@ outputs:
|
||||
project-dir:
|
||||
description: Directory containing the packageManager file used for pnpm resolution.
|
||||
value: ${{ steps.setup-pnpm.outputs.project-dir }}
|
||||
store-cache-hit:
|
||||
description: Whether the pnpm store cache restored an exact key.
|
||||
value: ${{ steps.pnpm-store-cache.outputs.cache-hit }}
|
||||
store-cache-primary-key:
|
||||
description: Exact pnpm store cache key used for restore/save.
|
||||
value: ${{ steps.pnpm-store-cache.outputs.cache-primary-key }}
|
||||
store-path:
|
||||
description: Resolved pnpm store path.
|
||||
value: ${{ steps.pnpm-store.outputs.path }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -71,12 +62,6 @@ runs:
|
||||
;;
|
||||
esac
|
||||
corepack enable
|
||||
for attempt in 1 2 3; do
|
||||
if corepack prepare "$package_manager" --activate; then
|
||||
exit 0
|
||||
fi
|
||||
sleep $((attempt * 5))
|
||||
done
|
||||
corepack prepare "$package_manager" --activate
|
||||
|
||||
- name: Resolve pnpm store path
|
||||
@@ -90,15 +75,14 @@ runs:
|
||||
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store cache
|
||||
id: pnpm-store-cache
|
||||
if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.path }}
|
||||
key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-${{ hashFiles(inputs.lockfile-path) }}
|
||||
key: pnpm-store-${{ runner.os }}-${{ inputs.node-version }}-${{ hashFiles(inputs.lockfile-path) }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-
|
||||
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-
|
||||
pnpm-store-${{ runner.os }}-${{ inputs.node-version }}-
|
||||
pnpm-store-${{ runner.os }}-
|
||||
|
||||
- name: Record pnpm version
|
||||
id: pnpm-version
|
||||
|
||||
@@ -8,10 +8,7 @@ openclaw_node_version_matches() {
|
||||
fi
|
||||
case "$requested" in
|
||||
*x)
|
||||
[[ "${actual%%.*}" == "${requested%%.*}" ]] || return 1
|
||||
if [[ "${requested%%.*}" == "22" ]]; then
|
||||
openclaw_node_version_at_least "$actual" "22.19.0"
|
||||
fi
|
||||
[[ "${actual%%.*}" == "${requested%%.*}" ]]
|
||||
;;
|
||||
*.*.*)
|
||||
[[ "$actual" == "$requested" ]]
|
||||
@@ -25,44 +22,20 @@ openclaw_node_version_matches() {
|
||||
esac
|
||||
}
|
||||
|
||||
openclaw_node_version_at_least() {
|
||||
local actual="$1"
|
||||
local minimum="$2"
|
||||
local actual_major actual_minor actual_patch minimum_major minimum_minor minimum_patch
|
||||
IFS=. read -r actual_major actual_minor actual_patch <<< "$actual"
|
||||
IFS=. read -r minimum_major minimum_minor minimum_patch <<< "$minimum"
|
||||
actual_minor="${actual_minor:-0}"
|
||||
actual_patch="${actual_patch:-0}"
|
||||
minimum_minor="${minimum_minor:-0}"
|
||||
minimum_patch="${minimum_patch:-0}"
|
||||
|
||||
if (( actual_major != minimum_major )); then
|
||||
(( actual_major > minimum_major ))
|
||||
return
|
||||
fi
|
||||
if (( actual_minor != minimum_minor )); then
|
||||
(( actual_minor > minimum_minor ))
|
||||
return
|
||||
fi
|
||||
(( actual_patch >= minimum_patch ))
|
||||
}
|
||||
|
||||
openclaw_active_node_version() {
|
||||
node -p 'process.versions.node' 2>/dev/null || true
|
||||
}
|
||||
|
||||
openclaw_prepend_node_bin() {
|
||||
local node_bin_dir="$1"
|
||||
local github_path_dir="${2:-$node_bin_dir}"
|
||||
local shell_node_bin_dir="$node_bin_dir"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
shell_node_bin_dir="$(cygpath -u "$node_bin_dir" 2>/dev/null || printf '%s' "$node_bin_dir")"
|
||||
fi
|
||||
export PATH="$shell_node_bin_dir:$PATH"
|
||||
if [[ -n "${GITHUB_PATH:-}" ]]; then
|
||||
local github_node_bin_dir="$github_path_dir"
|
||||
if [[ $# -lt 2 ]] && command -v cygpath >/dev/null 2>&1; then
|
||||
github_node_bin_dir="$shell_node_bin_dir"
|
||||
local github_node_bin_dir="$shell_node_bin_dir"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
github_node_bin_dir="$(cygpath -w "$shell_node_bin_dir" 2>/dev/null || printf '%s' "$shell_node_bin_dir")"
|
||||
fi
|
||||
echo "$github_node_bin_dir" >> "$GITHUB_PATH"
|
||||
@@ -84,9 +57,6 @@ openclaw_find_toolcache_node() {
|
||||
"/Users/runner/hostedtoolcache" \
|
||||
"/c/hostedtoolcache/windows"
|
||||
do
|
||||
if [[ ! -d "$root" && "$root" == *\\* ]] && command -v cygpath >/dev/null 2>&1; then
|
||||
root="$(cygpath -u "$root" 2>/dev/null || printf '%s' "$root")"
|
||||
fi
|
||||
if [[ -d "$root/node" ]]; then
|
||||
roots+=("$root/node")
|
||||
elif [[ "$(basename "$root")" == "node" && -d "$root" ]]; then
|
||||
@@ -95,7 +65,7 @@ openclaw_find_toolcache_node() {
|
||||
done
|
||||
|
||||
local node_root candidate candidate_version
|
||||
for node_root in ${roots[@]+"${roots[@]}"}; do
|
||||
for node_root in "${roots[@]}"; do
|
||||
while IFS= read -r candidate; do
|
||||
candidate_version="$("$candidate" -p 'process.versions.node' 2>/dev/null || true)"
|
||||
if openclaw_node_version_matches "$candidate_version" "$requested_node"; then
|
||||
@@ -138,10 +108,6 @@ openclaw_node_download_platform() {
|
||||
Linux:aarch64 | Linux:arm64) printf 'linux-arm64\n' ;;
|
||||
Darwin:x86_64) printf 'darwin-x64\n' ;;
|
||||
Darwin:arm64) printf 'darwin-arm64\n' ;;
|
||||
MINGW*:x86_64 | MSYS*:x86_64 | CYGWIN*:x86_64 | MINGW*:AMD64 | MSYS*:AMD64 | CYGWIN*:AMD64)
|
||||
printf 'win-x64\n'
|
||||
;;
|
||||
MINGW*:aarch64 | MINGW*:arm64 | MSYS*:aarch64 | MSYS*:arm64 | CYGWIN*:aarch64 | CYGWIN*:arm64) printf 'win-arm64\n' ;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
@@ -150,47 +116,15 @@ openclaw_node_download_platform() {
|
||||
|
||||
openclaw_download_node() {
|
||||
local requested_node="$1"
|
||||
local version platform archive_url install_root temp_root
|
||||
local version platform archive_url install_root
|
||||
version="$(openclaw_resolve_node_download_version "$requested_node")"
|
||||
platform="$(openclaw_node_download_platform)" || return 1
|
||||
temp_root="${RUNNER_TEMP:-/tmp}"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
temp_root="$(cygpath -u "$temp_root" 2>/dev/null || printf '%s\n' "$temp_root")"
|
||||
fi
|
||||
install_root="${temp_root}/openclaw-node-${version}-${platform}"
|
||||
if [[ "$platform" == win-* ]]; then
|
||||
local archive_path ps_archive_path ps_install_root ps_bin_dir node_bin_dir
|
||||
archive_path="${temp_root}/node-${version}-${platform}.zip"
|
||||
archive_url="https://nodejs.org/dist/${version}/node-${version}-${platform}.zip"
|
||||
rm -rf "$install_root"
|
||||
mkdir -p "$install_root"
|
||||
echo "Downloading Node ${version} from ${archive_url}"
|
||||
curl -fsSL -o "$archive_path" "$archive_url"
|
||||
ps_archive_path="$archive_path"
|
||||
ps_install_root="$install_root"
|
||||
if command -v cygpath >/dev/null 2>&1; then
|
||||
ps_archive_path="$(cygpath -w "$archive_path")"
|
||||
ps_install_root="$(cygpath -w "$install_root")"
|
||||
fi
|
||||
ps_bin_dir="$ps_install_root\\node-${version}-${platform}"
|
||||
node_bin_dir="$install_root/node-${version}-${platform}"
|
||||
if command -v pwsh >/dev/null 2>&1; then
|
||||
pwsh -NoLogo -NoProfile -Command "Expand-Archive -LiteralPath '${ps_archive_path}' -DestinationPath '${ps_install_root}' -Force"
|
||||
openclaw_prepend_node_bin "$node_bin_dir" "$ps_bin_dir"
|
||||
elif command -v powershell.exe >/dev/null 2>&1; then
|
||||
powershell.exe -NoLogo -NoProfile -Command "Expand-Archive -LiteralPath '${ps_archive_path}' -DestinationPath '${ps_install_root}' -Force"
|
||||
openclaw_prepend_node_bin "$node_bin_dir" "$ps_bin_dir"
|
||||
else
|
||||
unzip -q "$archive_path" -d "$install_root"
|
||||
openclaw_prepend_node_bin "$node_bin_dir"
|
||||
fi
|
||||
else
|
||||
archive_url="https://nodejs.org/dist/${version}/node-${version}-${platform}.tar.xz"
|
||||
mkdir -p "$install_root"
|
||||
echo "Downloading Node ${version} from ${archive_url}"
|
||||
curl -fsSL "$archive_url" | tar -xJ -C "$install_root" --strip-components=1
|
||||
openclaw_prepend_node_bin "$install_root/bin"
|
||||
fi
|
||||
install_root="${RUNNER_TEMP:-/tmp}/openclaw-node-${version}-${platform}"
|
||||
archive_url="https://nodejs.org/dist/${version}/node-${version}-${platform}.tar.xz"
|
||||
mkdir -p "$install_root"
|
||||
echo "Downloading Node ${version} from ${archive_url}"
|
||||
curl -fsSL "$archive_url" | tar -xJ -C "$install_root" --strip-components=1
|
||||
openclaw_prepend_node_bin "$install_root/bin"
|
||||
}
|
||||
|
||||
openclaw_ensure_node() {
|
||||
|
||||
@@ -17,8 +17,7 @@ paths:
|
||||
- src/acp/control-plane
|
||||
- src/agents/command
|
||||
- src/agents/cli-runner
|
||||
- src/agents/embedded-agent-runner
|
||||
- src/agents/sessions
|
||||
- src/agents/pi-embedded-runner
|
||||
- src/agents/tools
|
||||
- src/agents/*completion*.ts
|
||||
- src/agents/*transport*.ts
|
||||
|
||||
@@ -19,7 +19,7 @@ paths:
|
||||
- src/config/types.channel*.ts
|
||||
- src/gateway/server-channel*.ts
|
||||
- src/gateway/server-methods/channels.ts
|
||||
- packages/gateway-protocol/src/schema/channels.ts
|
||||
- src/gateway/protocol/schema/channels.ts
|
||||
- src/infra/channel-*.ts
|
||||
- src/infra/exec-approval-channel-runtime.ts
|
||||
- src/infra/outbound/channel-*.ts
|
||||
|
||||
@@ -22,15 +22,13 @@ paths:
|
||||
- src/agents/sandbox
|
||||
- src/agents/sandbox.ts
|
||||
- src/agents/sandbox-*.ts
|
||||
- src/agents/sessions/*auth*.ts
|
||||
- src/agents/sessions/**/*auth*.ts
|
||||
- src/cron/service/jobs.ts
|
||||
- src/cron/stagger.ts
|
||||
- src/gateway/*auth*.ts
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- packages/gateway-protocol/src/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
|
||||
@@ -30,7 +30,7 @@ paths:
|
||||
- src/gateway/**/*auth*.ts
|
||||
- src/gateway/*secret*.ts
|
||||
- src/gateway/**/*secret*.ts
|
||||
- packages/gateway-protocol/src/**/*secret*.ts
|
||||
- src/gateway/protocol/**/*secret*.ts
|
||||
- src/gateway/resolve-configured-secret-input-string*.ts
|
||||
- src/gateway/security-path*.ts
|
||||
- src/gateway/server-methods/secrets*.ts
|
||||
|
||||
@@ -15,7 +15,7 @@ query-filters:
|
||||
|
||||
paths:
|
||||
- src/gateway/method-scopes.ts
|
||||
- packages/gateway-protocol/src
|
||||
- src/gateway/protocol
|
||||
- src/gateway/server-methods
|
||||
- src/gateway/server-methods.ts
|
||||
- src/gateway/server-methods-list.ts
|
||||
|
||||
@@ -24,15 +24,14 @@ paths:
|
||||
- src/agents/openclaw-plugin-tools.ts
|
||||
- src/agents/openclaw-tools.runtime.ts
|
||||
- src/agents/openclaw-tools.registration.ts
|
||||
- src/agents/agent-tool-definition-adapter.ts
|
||||
- src/agents/agent-tools.abort.ts
|
||||
- src/agents/agent-tools.before-tool-call*.ts
|
||||
- src/agents/agent-tools.read.ts
|
||||
- src/agents/agent-tools-parameter-schema.ts
|
||||
- src/agents/sessions/tools/**
|
||||
- src/agents/embedded-agent-runner/effective-tool-policy.ts
|
||||
- src/agents/embedded-agent-runner/tool-name-allowlist.ts
|
||||
- src/agents/embedded-agent-runner/tool-schema-runtime.ts
|
||||
- src/agents/pi-tool-definition-adapter.ts
|
||||
- src/agents/pi-tools.abort.ts
|
||||
- src/agents/pi-tools.before-tool-call*.ts
|
||||
- src/agents/pi-tools.host-edit.ts
|
||||
- src/agents/pi-tools-parameter-schema.ts
|
||||
- src/agents/pi-embedded-runner/effective-tool-policy.ts
|
||||
- src/agents/pi-embedded-runner/tool-name-allowlist.ts
|
||||
- src/agents/pi-embedded-runner/tool-schema-runtime.ts
|
||||
- src/agents/tools/gateway-tool.ts
|
||||
- src/agents/tools/message-tool.ts
|
||||
- src/agents/tools/sessions-send-tool.ts
|
||||
|
||||
@@ -7,17 +7,8 @@ queries:
|
||||
- uses: ./.github/codeql/openclaw-boundary/queries/managed-proxy-runtime-mutation.ql
|
||||
|
||||
paths:
|
||||
- src/cli/gateway-cli/run-loop.ts
|
||||
- src/infra/gateway-lock.ts
|
||||
- src/infra/jsonl-socket.ts
|
||||
- src/infra/net
|
||||
- src/infra/push-apns-http2.ts
|
||||
- src/infra/ssh-tunnel.ts
|
||||
- src/proxy-capture
|
||||
- extensions/codex-supervisor/src/json-rpc-client.ts
|
||||
- extensions/irc/src
|
||||
- extensions/qa-lab/src
|
||||
- packages/net-policy/src
|
||||
- src
|
||||
- extensions
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
|
||||
@@ -15,6 +15,7 @@ query-filters:
|
||||
|
||||
paths:
|
||||
- src/infra/net
|
||||
- src/shared/net
|
||||
- src/agents/tools/web-fetch.ts
|
||||
- src/agents/tools/web-guarded-fetch.ts
|
||||
- src/agents/tools/web-shared.ts
|
||||
@@ -22,7 +23,6 @@ paths:
|
||||
- src/web-fetch
|
||||
- src/web/provider-runtime-shared.ts
|
||||
- packages/memory-host-sdk/src/host/ssrf-policy.ts
|
||||
- packages/net-policy/src
|
||||
|
||||
paths-ignore:
|
||||
- "**/node_modules"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user