Skip to content

Commit 3c8af7c

Browse files
author
Clever
committed
Merge remote-tracking branch 'origin/main' into fix/session-history-family
# Conflicts: # apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift # scripts/protocol-gen-swift.ts
2 parents 17feeab + 4a4a32a commit 3c8af7c

1,294 files changed

Lines changed: 74880 additions & 18509 deletions

File tree

Some content is hidden

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

.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs

Lines changed: 90 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { execFileSync } from "node:child_process";
44
import { readFileSync, writeFileSync } from "node:fs";
5+
import { pathToFileURL } from "node:url";
56

67
const repo = "openclaw/openclaw";
78
const commitAssociationQueryBatchSize = 20;
@@ -221,6 +222,42 @@ function referencesIn(text) {
221222
return references;
222223
}
223224

225+
function referenceLabelsIn(text) {
226+
const labels = [];
227+
for (const match of text.matchAll(
228+
/(?<![A-Za-z0-9_.&-])(?:(?<owner>[A-Za-z0-9_.-]+)\/(?<name>[A-Za-z0-9_.-]+))?#(?<number>\d+)/g,
229+
)) {
230+
const qualifiedRepository = match.groups?.owner
231+
? `${match.groups.owner}/${match.groups.name}`
232+
: undefined;
233+
labels.push(
234+
!qualifiedRepository || qualifiedRepository.toLowerCase() === repo
235+
? `#${match.groups?.number}`
236+
: `${qualifiedRepository}#${match.groups?.number}`,
237+
);
238+
}
239+
return labels;
240+
}
241+
242+
export function renderContributionRecordEntry(entry) {
243+
const references = [];
244+
appendUnique(references, referenceLabelsIn(entry.title));
245+
appendUnique(
246+
references,
247+
(entry.priorReferences ?? []).map((number) => `#${number}`),
248+
);
249+
appendUnique(references, entry.externalReferences ?? []);
250+
for (const issue of entry.linkedIssues) {
251+
appendUnique(references, [`#${issue.number}`]);
252+
}
253+
const related = references.length > 0 ? ` Related ${references.join(", ")}.` : "";
254+
const attribution =
255+
entry.thanks.length > 0
256+
? ` Thanks ${entry.thanks.map((handle) => `@${handle}`).join(" and ")}.`
257+
: "";
258+
return `- **PR #${entry.number}**${related}${attribution}`;
259+
}
260+
224261
function closingReferencesIn(text) {
225262
const references = [];
226263
for (const match of text.matchAll(
@@ -255,26 +292,38 @@ function handlesIn(text) {
255292
);
256293
}
257294

258-
function relatedReferencesIn(line) {
259-
const related = line.match(/\bRelated ((?:#\d+)(?:, #\d+)*)\./);
260-
return related ? referencesIn(related[1]) : [];
295+
function externalReferencesIn(text) {
296+
return referenceLabelsIn(text).filter((reference) => !reference.startsWith("#"));
297+
}
298+
299+
function appendUnique(values, additions) {
300+
const seen = new Set(values.map((value) => value.toLowerCase()));
301+
for (const value of additions) {
302+
const key = value.toLowerCase();
303+
if (!seen.has(key)) {
304+
values.push(value);
305+
seen.add(key);
306+
}
307+
}
261308
}
262309

263310
function addContributionRecordEntry(entries, key, entry) {
264311
const existing = entries.get(key);
265312
if (!existing) {
266313
entries.set(key, {
267314
...entry,
315+
externalReferences: [...(entry.externalReferences ?? [])],
268316
references: [...entry.references],
269317
thanks: [...entry.thanks],
270318
});
271319
return;
272320
}
321+
appendUnique(existing.externalReferences, entry.externalReferences ?? []);
273322
appendReferences(existing.references, entry.references);
274323
addHandles(existing.thanks, entry.thanks);
275324
}
276325

277-
function contributionRecordFor(section) {
326+
export function contributionRecordFor(section) {
278327
const result = { legacyIssues: new Map(), pullRequests: new Map() };
279328
const recordStart = section.source.search(/\n### Complete contribution (?:ledger|record)\r?$/m);
280329
if (recordStart < 0) {
@@ -301,8 +350,10 @@ function contributionRecordFor(section) {
301350
const number = explicitRecord?.[1] ?? legacyRecord?.[1];
302351
if (number) {
303352
const value = Number(number);
353+
const metadata = explicitRecord ? line.slice(explicitRecord[0].length) : line;
304354
addContributionRecordEntry(result.pullRequests, value, {
305-
references: relatedReferencesIn(line),
355+
externalReferences: externalReferencesIn(metadata),
356+
references: referencesIn(metadata).filter((reference) => reference !== value),
306357
thanks: handlesIn(line),
307358
});
308359
}
@@ -345,6 +396,7 @@ function withoutRevertedContributionRecords(record, revertedReferences) {
345396
}
346397
addContributionRecordEntry(filtered.pullRequests, number, {
347398
...entry,
399+
externalReferences: entry.externalReferences,
348400
references: entry.references.filter((reference) => !revertedReferences.has(reference)),
349401
});
350402
}
@@ -1153,7 +1205,7 @@ function mergeIssues(...groups) {
11531205
return [...entries.values()];
11541206
}
11551207

1156-
function ledgerFor(
1208+
export function ledgerFor(
11571209
base,
11581210
target,
11591211
references,
@@ -1203,6 +1255,8 @@ function ledgerFor(
12031255
const issues = entries.filter((entry) => entry.type === "Issue");
12041256
const legacyIssues = legacyIssuesByPullRequest(priorRecord, nodes);
12051257
const records = pullRequests.map((entry) => {
1258+
const priorEntry = priorRecord.pullRequests.get(entry.number);
1259+
const priorReferences = priorEntry?.references ?? [];
12061260
const titleIssues = issueEntries(referencesIn(entry.title), nodes);
12071261
const closingIssues = issueEntries(
12081262
entry.closingIssuesReferences?.nodes.map((issue) => issue.number) ?? [],
@@ -1212,41 +1266,31 @@ function ledgerFor(
12121266
titleIssues,
12131267
closingIssues,
12141268
relationships.issuesByPullRequest.get(entry.number) ?? [],
1269+
issueEntries(priorReferences, nodes),
12151270
issueEntries(legacyIssues.get(entry.number) ?? [], nodes, priorRecord.legacyIssues),
12161271
);
1217-
const titleIssueNumbers = new Set(titleIssues.map((issue) => issue.number));
1218-
const relatedIssues = linkedIssues.filter((issue) => !titleIssueNumbers.has(issue.number));
12191272
const thanks = [...entry.thanks];
1273+
addHandles(thanks, priorEntry?.thanks ?? []);
12201274
for (const issue of linkedIssues) {
12211275
addHandles(thanks, issue.thanks);
12221276
}
12231277
return {
12241278
...entry,
12251279
...editorialClassification(entry.title),
1280+
externalReferences: priorEntry?.externalReferences ?? [],
12261281
linkedIssues,
1227-
relatedIssues,
1282+
priorReferences,
12281283
thanks,
12291284
};
12301285
});
1231-
const renderEntry = (entry) => {
1232-
const attribution =
1233-
entry.thanks.length > 0
1234-
? ` Thanks ${entry.thanks.map((handle) => `@${handle}`).join(" and ")}.`
1235-
: "";
1236-
const relatedIssues =
1237-
entry.relatedIssues.length > 0
1238-
? ` Related ${entry.relatedIssues.map((issue) => `#${issue.number}`).join(", ")}.`
1239-
: "";
1240-
return `- **PR #${entry.number}** ${withSentenceEnding(entry.title)}${relatedIssues}${attribution}`;
1241-
};
12421286
const ledger = [
12431287
"### Complete contribution record",
12441288
"",
12451289
`This audited record covers the complete ${base}..${target} history: ${records.length} merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.`,
12461290
"",
12471291
"#### Pull requests",
12481292
"",
1249-
...records.map((entry) => renderEntry(entry)),
1293+
...records.map((entry) => renderContributionRecordEntry(entry)),
12501294
].join("\n");
12511295
return {
12521296
entries,
@@ -1267,7 +1311,7 @@ function replaceLedger(changelog, section, ledger, pullRequests, directCommits)
12671311
return `${changelog.slice(0, section.start)}${replacement}${changelog.slice(section.end)}`;
12681312
}
12691313

1270-
function ledgerChecks(section, pullRequests, nodes, directCommits) {
1314+
export function ledgerChecks(section, pullRequests, nodes, directCommits) {
12711315
const errors = [];
12721316
if (/@undefined\b/i.test(section.source)) {
12731317
errors.push("release section contains invalid @undefined contributor credit");
@@ -1311,6 +1355,25 @@ function ledgerChecks(section, pullRequests, nodes, directCommits) {
13111355
errors.push(`missing Thanks @${handle} for #${entry.number}`);
13121356
}
13131357
}
1358+
const expectedReferences = [];
1359+
appendUnique(expectedReferences, referenceLabelsIn(entry.title));
1360+
appendUnique(
1361+
expectedReferences,
1362+
entry.priorReferences.map((number) => `#${number}`),
1363+
);
1364+
appendUnique(expectedReferences, entry.externalReferences);
1365+
appendUnique(
1366+
expectedReferences,
1367+
entry.linkedIssues.map((issue) => `#${issue.number}`),
1368+
);
1369+
const actualReferences = new Set(
1370+
referenceLabelsIn(line).map((reference) => reference.toLowerCase()),
1371+
);
1372+
for (const reference of expectedReferences) {
1373+
if (!actualReferences.has(reference.toLowerCase())) {
1374+
errors.push(`missing ${reference} on contribution record for PR #${entry.number}`);
1375+
}
1376+
}
13141377
}
13151378
const editorialProse = section.source.slice(0, ledgerStart);
13161379
for (const entry of pullRequests) {
@@ -1402,6 +1465,8 @@ function manifestFor(options, source, ledger, directCommitRecords) {
14021465
type: entry.type,
14031466
editorialEligible: entry.editorialEligible,
14041467
thanks: entry.thanks,
1468+
externalReferences: entry.externalReferences,
1469+
relatedReferences: [...new Set([...entry.priorReferences, ...referencesIn(entry.title)])],
14051470
linkedIssues: entry.linkedIssues.map((issue) => ({
14061471
number: issue.number,
14071472
title: issue.title,
@@ -1620,4 +1685,6 @@ function main() {
16201685
}
16211686
}
16221687

1623-
main();
1688+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
1689+
main();
1690+
}

.agents/skills/openclaw-parallels-smoke/SKILL.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,17 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
99

1010
## Global rules
1111

12-
- Use the snapshot most closely matching the requested fresh baseline.
12+
- Inventory existing VMs and snapshots before provisioning anything. When a preconfigured pristine
13+
snapshot matches the requested baseline, switch to it and reuse its user, tools, and base setup.
14+
Do not create a new VM, reinstall macOS, or rebuild the guest baseline for a "fresh" run.
15+
- "Fresh" means restoring the closest existing pristine snapshot, not creating another snapshot.
16+
Do not create ad-hoc snapshots unless the user explicitly asks or no suitable baseline exists;
17+
restore the original snapshot and leave the guest stopped after an ad-hoc run.
18+
- Inspect the snapshot state before restoring it. A pristine `poweron` snapshot can contain the
19+
preconfigured logged-in session; switch to it normally so Parallels resumes that session. Do not
20+
pass `--skip-resume` at test entry unless the run intentionally needs to discard the saved session
21+
and boot from the login window. `--skip-resume` is acceptable for final cleanup that must leave the
22+
restored source guest stopped.
1323
- Gateway verification in smoke runs should use `openclaw gateway status --deep --require-rpc` unless the stable version being checked does not support it yet.
1424
- Stable `2026.3.12` pre-upgrade diagnostics may require a plain `gateway status --deep` fallback.
1525
- Treat `precheck=latest-ref-fail` on that stable pre-upgrade lane as baseline, not automatically a regression.

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

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
---
22
name: release-openclaw-maintainer
3-
description: Prepare or verify OpenClaw stable/beta releases, changelogs, release notes, publish commands, and artifacts.
3+
description: Prepare or verify OpenClaw stable, beta, and extended-stable releases, including backport discovery, changelogs, release notes, publish commands, and artifacts.
44
---
55

66
# OpenClaw Release Maintainer
77

8-
Use this skill for release and publish-time workflow. Load `$release-private` if it exists before resolving Peter-owned credential locators or private host topology. Keep ordinary development changes and GHSA-specific advisory work outside this skill.
8+
Use this skill for release and publish-time workflow, including preparing the
9+
approved backport set for an extended-stable maintenance release. Load
10+
`$release-private` if it exists before resolving Peter-owned credential
11+
locators or private host topology. Keep ordinary development changes and
12+
GHSA-specific advisory work outside this skill.
913

1014
## Respect release guardrails
1115

@@ -138,9 +142,64 @@ prepare-run <PR>`.
138142
- When asked to announce on X, use `~/Projects/bird/bird` and follow the
139143
release tweet style below.
140144

145+
## Prepare extended-stable backports
146+
147+
When asked to create the initial `.33` extended-stable line or a later
148+
maintenance patch, read
149+
`references/extended-stable-backports.md` and follow it before version, tag, or
150+
publication work. Treat backport discovery and preparation as an ability of
151+
this release skill, not as a separate release workflow.
152+
153+
The backport ability owns the complete mainline inventory, private-security
154+
reconciliation, candidate decisions, maintainer approval, coordinated staging
155+
PR, and proof handoff. After that PR lands, use the dedicated npm-only sequence
156+
below. Never route `.33+` through the regular beta/stable release sequence.
157+
158+
## Publish extended-stable releases
159+
160+
Use this path only for the trailing completed month's `.33+` line. Treat
161+
`docs/reference/RELEASING.md`,
162+
`scripts/openclaw-npm-extended-stable-release.mjs`, and the release workflows
163+
on pinned current `main` as the exact command and validation contract.
164+
165+
1. Check out the canonical `extended-stable/YYYY.M.33` branch after the
166+
approved backport PR lands. Require its tip, root package version, every
167+
publishable official plugin version, and intended immutable `vYYYY.M.P` tag
168+
to identify one exact release commit.
169+
2. Create and push `vYYYY.M.P` at that exact branch tip only after version prep
170+
and focused backport proof are complete.
171+
3. Dispatch `openclaw-npm-release.yml` with `preflight_only=true` and
172+
`npm_dist_tag=extended-stable` from the canonical branch. Save the successful
173+
npm preflight run ID.
174+
4. Dispatch `full-release-validation.yml` from the same branch with
175+
`ref=extended-stable/YYYY.M.33` and `release_profile=stable`. Save the
176+
successful exact-head validation run ID.
177+
5. Dispatch `plugin-npm-release.yml` from the same branch with
178+
`publish_scope=all-publishable`, the full release SHA as `ref`, and
179+
`npm_dist_tag=extended-stable`. Require complete exact-version and selector
180+
readback, then save the successful plugin run ID.
181+
6. Dispatch the real `openclaw-npm-release.yml` publish from the same branch
182+
with the intended tag, `npm_dist_tag=extended-stable`, and all three saved
183+
run IDs. The workflow must publish the exact prepared core tarball and prove
184+
the referenced runs match the canonical branch and release SHA.
185+
7. Independently verify the exact core package, every official plugin package,
186+
and all `extended-stable` selectors. If only the core selector readback
187+
fails, use the `openclaw` repair command generated by the core workflow. If
188+
an official-plugin selector is missing or stale for an already-published
189+
version, use the approved credential-isolated release tooling for manual
190+
plugin tag repair; the OIDC source workflow cannot mutate that tag. Never
191+
republish an immutable version.
192+
8. Do not create a GitHub Release or publish macOS, Windows, Docker, mobile,
193+
website, ClawHub, or private dist-tag artifacts from this path.
194+
141195
## Keep release channel naming aligned
142196

143-
- `stable`: tagged releases only, published to npm `beta` by default; operators may target npm `latest` explicitly or promote later
197+
- `stable`: user updates resolve npm `latest`; tagged regular releases publish
198+
to npm `beta` by default, then operators may target or promote to `latest`
199+
explicitly
200+
- `extended-stable`: user updates resolve npm `extended-stable`; operators
201+
publish the trailing completed month's `.33+` line from
202+
`extended-stable/YYYY.M.33`
144203
- `beta`: prerelease tags like `vYYYY.M.PATCH-beta.N`, with npm dist-tag `beta`
145204
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
146205
- `dev`: moving head on `main`

0 commit comments

Comments
 (0)