Skip to content

Commit b98f5b5

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/83999-cli-command-suggestions
2 parents f3879a3 + 8be3bee commit b98f5b5

252 files changed

Lines changed: 10058 additions & 2319 deletions

File tree

Some content is hidden

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

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

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,23 @@ pnpm release:check
317317
pnpm test:install:smoke
318318
```
319319

320+
- Before tagging, diff publishable plugin package manifests against the last
321+
reachable stable/beta release tag. For every newly publishable package
322+
(`openclaw.release.publishToNpm: true` or `publishToClawHub: true`) whose
323+
package name did not exist in the base tag, verify the target registry package
324+
already exists in npm/ClawHub or stop and help the owner mint/prepublish the
325+
package first. Do not hide or disable release surfaces just to unblock a
326+
train unless the owner explicitly decides the plugin should not ship in that
327+
release; first-package registry ownership is release prep, not product
328+
rollback. The mint/prepublish path must either be the real release publish
329+
path for the auto-bumped beta version, or a deliberately non-consuming
330+
registry-prep step that cannot occupy the next beta version/tag. Confirm
331+
registry owner, npm scope/package-creation permission, provenance path, and
332+
first-package publish plan before the full release publish continues. Useful
333+
npm probe:
334+
`npm view <package-name> version dist-tags --json --prefer-online`; a 404 for
335+
a package newly added to the release is a release-prep blocker, not something
336+
to discover from the publish job.
320337
- Use `pnpm qa:otel:smoke` when release validation needs telemetry coverage.
321338
It starts a local OTLP/HTTP trace receiver, runs QA-lab's
322339
`otel-trace-smoke`, and checks span names plus content/identifier redaction
@@ -562,7 +579,11 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
562579
- Use `NPM_TOKEN` only for explicit npm dist-tag management modes, because npm
563580
does not support trusted publishing for `npm dist-tag add`.
564581
- `@openclaw/*` plugin publishes use a separate maintainer-only flow.
565-
- Only publish plugins that already exist on npm; bundled disk-tree-only plugins stay unpublished.
582+
- Publishable plugins that are new to npm require owner-led first-package
583+
minting before the full release publish. Do not consume the next beta version
584+
with an ad-hoc manual package publish; use the release-owned auto-bumped
585+
version path, or a non-consuming registry setup/preflight step. Bundled
586+
disk-tree-only plugins stay unpublished.
566587

567588
## Fallback local mac publish
568589

@@ -619,7 +640,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
619640
mac app, signing, notarization, and appcast path.
620641
12. Confirm the target npm version is not already published.
621642
13. Create and push the git tag from the release branch.
622-
14. Create or refresh the matching GitHub release.
643+
14. Do not create or publish the matching GitHub release page yet. The real
644+
publish workflow creates or undrafts it only after postpublish verification
645+
and release evidence upload pass.
623646
15. Dispatch Actions > `QA-Lab - All Lanes` against the release tag and wait
624647
for the mock parity, live Matrix, and live Telegram credentialed-channel
625648
lanes to pass.
@@ -642,20 +665,29 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
642665
with `preflight_only=true` and wait for it to pass. Save that run id because
643666
the real publish requires it to reuse the notarized mac artifacts.
644667
21. If any preflight or validation run fails, fix the issue on a new commit,
645-
delete the tag and matching GitHub release, recreate them from the fixed
646-
commit, and rerun all relevant preflights from scratch before continuing.
647-
Never reuse old preflight results after the commit changes. For pushed or
648-
published beta tags, do not delete/recreate; increment to the next beta tag.
649-
For preflight-only failures where npm did not publish the beta version,
650-
delete/recreate the same beta tag and prerelease at the fixed commit instead
651-
of skipping a prerelease number.
668+
delete the tag and any accidental draft/incomplete GitHub release, recreate
669+
the tag from the fixed commit, and rerun all relevant preflights from
670+
scratch before continuing. Never reuse old preflight results after the
671+
commit changes. Once the npm version exists, do not rerun the publish
672+
workflow for that same version; finalize the existing draft/evidence state
673+
manually or cut a correction tag. For pushed or published beta tags, do not
674+
delete/recreate; increment to the next beta tag. For preflight-only failures
675+
where npm did not publish the beta version, delete/recreate the same beta
676+
tag and any accidental draft/incomplete prerelease at the fixed commit
677+
instead of skipping a prerelease number.
652678
22. Start `.github/workflows/openclaw-npm-release.yml` from the same branch with
653679
the same tag for the real publish, choose `npm_dist_tag` (`beta` default,
654680
`latest` only when you intentionally want direct stable publish), keep it
655681
the same as the preflight run, and pass the successful npm
656682
`preflight_run_id`.
657683
23. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`.
658-
24. Run postpublish verification:
684+
24. Wait for the real publish workflow to run postpublish verification,
685+
create or update the GitHub release as a draft, upload dependency evidence,
686+
append release verification proof, and only then undraft/publish it. If a
687+
waited plugin publish fails after OpenClaw npm succeeds, the workflow keeps
688+
the release draft with OpenClaw npm evidence and exits red; do not undraft
689+
until the plugin publish gap is repaired. The standalone verifier command
690+
remains the recovery probe:
659691
`node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>`.
660692
25. Run the post-published beta verification roster. First scan current `main`
661693
for critical fixes that landed after the release branch cut; backport only

.github/workflows/openclaw-performance.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,13 @@ jobs:
527527
cleanup_gateway
528528
trap - EXIT
529529
530+
if node -e "const fs=require('node:fs'); const scripts=require('./package.json').scripts||{}; process.exit(scripts['test:sqlite:perf:smoke'] && fs.existsSync('scripts/bench-sqlite-state.ts') ? 0 : 1)"; then
531+
pnpm test:sqlite:perf:smoke
532+
cp .artifacts/sqlite-perf/smoke.json "$SOURCE_PERF_DIR/sqlite-perf-smoke.json"
533+
else
534+
echo "SQLite state smoke probe is not available in ${TESTED_REF}; continuing with the remaining source probes." >> "$GITHUB_STEP_SUMMARY"
535+
fi
536+
530537
summary_args=(node "$PERFORMANCE_HELPER_DIR/scripts/openclaw-performance-source-summary.mjs" \
531538
--source-dir "$SOURCE_PERF_DIR" \
532539
--output "$SOURCE_PERF_DIR/index.md")
@@ -604,7 +611,7 @@ jobs:
604611
605612
## Source probes
606613
607-
Additional gateway boot, memory, plugin pressure, mock hello-loop, and CLI startup numbers are in [source/index.md](source/index.md).
614+
Additional gateway boot, memory, plugin pressure, mock hello-loop, CLI startup, and SQLite state smoke numbers are in [source/index.md](source/index.md).
608615
EOF
609616
fi
610617
fi

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

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,63 @@ jobs:
653653
done
654654
}
655655
656+
guard_existing_public_release() {
657+
local release_version asset_name release_json is_draft has_sha has_proof has_asset release_url
658+
659+
if [[ "${PUBLISH_OPENCLAW_NPM}" != "true" ]]; then
660+
return 0
661+
fi
662+
663+
if ! release_json="$(gh release view "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" --json isDraft,assets,body,url 2>/dev/null)"; then
664+
return 0
665+
fi
666+
667+
is_draft="$(printf '%s' "${release_json}" | jq -r '.isDraft')"
668+
if [[ "${is_draft}" == "true" ]]; then
669+
return 0
670+
fi
671+
672+
release_version="${RELEASE_TAG#v}"
673+
asset_name="openclaw-${release_version}-dependency-evidence.zip"
674+
has_sha="$(printf '%s' "${release_json}" | jq --arg sha "${TARGET_SHA}" -r '.body | contains($sha)')"
675+
has_proof="$(printf '%s' "${release_json}" | jq -r '.body | contains("### Release verification")')"
676+
has_asset="$(printf '%s' "${release_json}" | jq --arg name "${asset_name}" -r 'any(.assets[]?; .name == $name)')"
677+
release_url="$(printf '%s' "${release_json}" | jq -r '.url')"
678+
679+
if [[ "${has_sha}" == "true" && "${has_proof}" == "true" && "${has_asset}" == "true" ]]; then
680+
return 0
681+
fi
682+
683+
{
684+
echo "Release ${RELEASE_TAG} already has a public GitHub release page without complete postpublish evidence for ${TARGET_SHA}."
685+
echo "Refusing to reuse a public prerelease tag after publication started: ${release_url}"
686+
echo "Create a new beta tag or delete/draft the incomplete public release before retrying."
687+
} >&2
688+
exit 1
689+
}
690+
691+
guard_openclaw_npm_not_already_published() {
692+
local release_version release_url
693+
694+
if [[ "${PUBLISH_OPENCLAW_NPM}" != "true" ]]; then
695+
return 0
696+
fi
697+
698+
release_version="${RELEASE_TAG#v}"
699+
if ! npm view "openclaw@${release_version}" version >/dev/null 2>&1; then
700+
return 0
701+
fi
702+
703+
release_url="https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}"
704+
{
705+
echo "openclaw@${release_version} is already published on npm."
706+
echo "Refusing to dispatch publish child workflows for an already-published version."
707+
echo "If this is recovery from a failed postpublish evidence or draft-release step, repair/finalize the existing draft or create a correction tag; do not rerun the publish workflow for the same npm version."
708+
echo "Release page, if present: ${release_url}"
709+
} >&2
710+
exit 1
711+
}
712+
656713
create_or_update_github_release() {
657714
local release_version notes_version title notes_file changelog_file latest_arg prerelease_args
658715
release_version="${RELEASE_TAG#v}"
@@ -698,11 +755,17 @@ jobs:
698755
else
699756
gh release create "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" \
700757
--verify-tag \
758+
--draft \
701759
--title "${title}" \
702760
--notes-file "${notes_file}" \
703761
"${prerelease_args[@]}" \
704762
"${latest_arg}"
705763
fi
764+
echo "- GitHub release draft: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" >> "$GITHUB_STEP_SUMMARY"
765+
}
766+
767+
publish_github_release() {
768+
gh release edit "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" --draft=false
706769
echo "- GitHub release: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" >> "$GITHUB_STEP_SUMMARY"
707770
}
708771
@@ -735,9 +798,11 @@ jobs:
735798
}
736799
737800
verify_published_release() {
738-
local release_version evidence_path
801+
local release_version evidence_path skip_clawhub
739802
local -a verify_args
740803
804+
skip_clawhub="${1:-false}"
805+
741806
release_version="${RELEASE_TAG#v}"
742807
evidence_path="${POSTPUBLISH_EVIDENCE_DIR}/release-postpublish-evidence.json"
743808
mkdir -p "${POSTPUBLISH_EVIDENCE_DIR}"
@@ -754,11 +819,12 @@ jobs:
754819
--plugin-npm-run "${plugin_npm_run_id}"
755820
--openclaw-npm-run "${openclaw_npm_run_id}"
756821
--evidence-out "${evidence_path}"
822+
--skip-github-release
757823
)
758-
if [[ "${WAIT_FOR_CLAWHUB}" == "true" ]]; then
759-
verify_args+=(--plugin-clawhub-run "${plugin_clawhub_run_id}")
760-
else
824+
if [[ "${skip_clawhub}" == "true" || "${WAIT_FOR_CLAWHUB}" != "true" ]]; then
761825
verify_args+=(--skip-clawhub)
826+
else
827+
verify_args+=(--plugin-clawhub-run "${plugin_clawhub_run_id}")
762828
fi
763829
if [[ -n "${PLUGINS// }" ]]; then
764830
verify_args+=(--plugins "${PLUGINS}")
@@ -799,6 +865,7 @@ jobs:
799865
RELEASE_NOTES_FILE="${notes_file}" \
800866
RELEASE_VERSION="${release_version}" \
801867
RELEASE_TAG="${RELEASE_TAG}" \
868+
RELEASE_SHA="${TARGET_SHA}" \
802869
RELEASE_REPO="${GITHUB_REPOSITORY}" \
803870
RELEASE_TARBALL="${tarball}" \
804871
RELEASE_INTEGRITY="${integrity}" \
@@ -825,6 +892,7 @@ jobs:
825892
`- npm package: https://www.npmjs.com/package/openclaw/v/${process.env.RELEASE_VERSION}`,
826893
`- registry tarball: ${process.env.RELEASE_TARBALL}`,
827894
`- integrity: \`${process.env.RELEASE_INTEGRITY}\``,
895+
`- release SHA: \`${process.env.RELEASE_SHA}\``,
828896
`- full release CI report: https://github.com/openclaw/releases/blob/main/evidence/${process.env.RELEASE_VERSION}/release-evidence.md`,
829897
`- release publish: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.RELEASE_PUBLISH_RUN_ID}`,
830898
`- npm preflight: https://github.com/${process.env.RELEASE_REPO}/actions/runs/${process.env.PREFLIGHT_RUN_ID}`,
@@ -863,6 +931,9 @@ jobs:
863931
fi
864932
} >> "$GITHUB_STEP_SUMMARY"
865933
934+
guard_existing_public_release
935+
guard_openclaw_npm_not_already_published
936+
866937
npm_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}" -f release_publish_run_id="${GITHUB_RUN_ID}")
867938
clawhub_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}" -f release_publish_run_id="${GITHUB_RUN_ID}")
868939
if [[ -n "${PLUGINS}" ]]; then
@@ -934,21 +1005,27 @@ jobs:
9341005
openclaw_failed=1
9351006
fi
9361007
937-
if [[ -n "${openclaw_npm_run_id}" && "${openclaw_failed}" == "0" ]]; then
938-
create_or_update_github_release
939-
upload_dependency_evidence_release_asset
940-
fi
941-
9421008
if [[ -n "${clawhub_pid}" ]] && ! wait "${clawhub_pid}"; then
9431009
failed=1
9441010
fi
9451011
if [[ -f "${clawhub_result}" && "$(cat "${clawhub_result}")" != "success" ]]; then
9461012
failed=1
9471013
fi
9481014
949-
if [[ "${failed}" == "0" && -n "${openclaw_npm_run_id}" ]]; then
950-
verify_published_release
1015+
if [[ -n "${openclaw_npm_run_id}" && "${openclaw_failed}" == "0" ]]; then
1016+
if [[ "${failed}" == "0" ]]; then
1017+
verify_published_release
1018+
else
1019+
verify_published_release true
1020+
fi
1021+
create_or_update_github_release
1022+
upload_dependency_evidence_release_asset
9511023
append_release_proof_to_github_release
1024+
if [[ "${failed}" == "0" ]]; then
1025+
publish_github_release
1026+
else
1027+
echo "- GitHub release: left as draft because a required publish child failed" >> "$GITHUB_STEP_SUMMARY"
1028+
fi
9521029
fi
9531030
if [[ "${failed}" != "0" ]]; then
9541031
exit 1

.github/workflows/plugin-clawhub-release.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,6 @@ jobs:
197197
exit 1
198198
fi
199199
200-
- name: Verify OpenClaw ClawHub package ownership
201-
if: steps.plan.outputs.has_candidates == 'true'
202-
env:
203-
CLAWHUB_REGISTRY: ${{ env.CLAWHUB_REGISTRY }}
204-
run: node --import tsx scripts/plugin-clawhub-owner-preflight.ts .local/plugin-clawhub-release-plan.json
205-
206200
validate_release_publish_approval:
207201
name: Validate release publish approval
208202
needs: preview_plugins_clawhub

apps/macos/Sources/OpenClaw/CronJobsStore.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class CronJobsStore {
7272
do {
7373
if let status = try? await GatewayConnection.shared.cronStatus() {
7474
self.schedulerEnabled = status.enabled
75-
self.schedulerStorePath = status.storePath
75+
self.schedulerStorePath = status.sqlitePath ?? status.storePath
7676
self.schedulerNextWakeAtMs = status.nextWakeAtMs
7777
}
7878
self.jobs = try await GatewayConnection.shared.cronList(includeDisabled: true)

apps/macos/Sources/OpenClaw/GatewayConnection.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ extension GatewayConnection {
775775
struct CronSchedulerStatus: Decodable {
776776
let enabled: Bool
777777
let storePath: String
778+
let sqlitePath: String?
778779
let jobs: Int
779780
let nextWakeAtMs: Int?
780781
}

docs/ci.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ The workflow installs OCM from a pinned release and Kova from `openclaw/Kova` at
183183
- `mock-deep-profile`: CPU/heap/trace profiling for startup, gateway, and agent-turn hotspots.
184184
- `live-openai-candidate`: a real OpenAI `openai/gpt-5.5` agent turn, skipped when `OPENAI_API_KEY` is unavailable.
185185

186-
The mock-provider lane also runs OpenClaw-native source probes after the Kova pass: gateway boot timing and memory across default, hook, and 50-plugin startup cases; bundled plugin import RSS, repeated mock-OpenAI `channel-chat-baseline` hello loops, and CLI startup commands against the booted gateway. When the previous published mock-provider source report is available for the tested ref, the source summary compares current RSS and heap values against that baseline and marks large RSS increases as `watch`. The source probe Markdown summary lives at `source/index.md` in the report bundle, with raw JSON beside it.
186+
The mock-provider lane also runs OpenClaw-native source probes after the Kova pass: gateway boot timing and memory across default, hook, and 50-plugin startup cases; bundled plugin import RSS, repeated mock-OpenAI `channel-chat-baseline` hello loops, CLI startup commands against the booted gateway, and the SQLite state smoke performance probe. When the previous published mock-provider source report is available for the tested ref, the source summary compares current RSS and heap values against that baseline and marks large RSS increases as `watch`. The source probe Markdown summary lives at `source/index.md` in the report bundle, with raw JSON beside it.
187187

188188
Every lane uploads GitHub artifacts. When `CLAWGRIT_REPORTS_TOKEN` is configured, the workflow also commits `report.json`, `report.md`, bundles, `index.md`, and source-probe artifacts into `openclaw/clawgrit-reports` under `openclaw-performance/<tested-ref>/<run-id>-<attempt>/<lane>/`. The current tested-ref pointer is written as `openclaw-performance/<tested-ref>/latest-<lane>.json`.
189189

docs/docs.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,6 @@
13761376
"clawhub/publishing",
13771377
"clawhub/plugin-validation-fixes",
13781378
"clawhub/skill-format",
1379-
"clawhub/soul-format",
13801379
"clawhub/auth",
13811380
"clawhub/telemetry",
13821381
"clawhub/troubleshooting"

docs/gateway/configuration-examples.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ example `~/.agents/skills/manager -> ~/Projects/manager/skills`.
493493
- `extraDirs` scans the sibling repo as an explicit skill root.
494494
- `allowSymlinkTargets` lets symlinked skill folders resolve into that trusted
495495
real target root without allowing arbitrary symlink escapes.
496+
- To let Skill Workshop apply write through the same trusted symlink target,
497+
set `skills.workshop.allowSymlinkTargetWrites: true`.
496498

497499
## Common patterns
498500

docs/gateway/configuration-reference.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ See [MCP](/cli/mcp#openclaw-as-an-mcp-client-registry) and
200200
nodeManager: "npm", // npm | pnpm | yarn | bun
201201
allowUploadedArchives: false,
202202
},
203+
workshop: {
204+
allowSymlinkTargetWrites: false,
205+
},
203206
entries: {
204207
"image-lab": {
205208
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" }, // or plaintext string
@@ -216,6 +219,8 @@ See [MCP](/cli/mcp#openclaw-as-an-mcp-client-registry) and
216219
- `load.extraDirs`: extra shared skill roots (lowest precedence).
217220
- `load.allowSymlinkTargets`: trusted real target roots that skill symlinks may
218221
resolve into when the link lives outside its configured source root.
222+
- `workshop.allowSymlinkTargetWrites`: allows Skill Workshop apply to write
223+
through already-trusted symlink targets (default: false).
219224
- `install.preferBrew`: when true, prefer Homebrew installers when `brew` is
220225
available before falling back to other installer kinds.
221226
- `install.nodeManager`: node installer preference for `metadata.openclaw.install`

0 commit comments

Comments
 (0)