Commit Graph

7497 Commits

Author SHA1 Message Date
Peter Steinberger 1fc29beba2 refactor(state): fold singleton tables into config_machine_state at schema v12 (#129876)
* refactor(state): fold singleton tables into config_machine_state at schema v11

Eight singleton tables (skill_curator_state, update_check_state,
clawhub_promotions_feed_state, model_catalog_remote, voicewake_triggers,
voicewake_routing_config, voicewake_routing_routes,
onboarding_recommendations) were each one logical JSON value behind a
fixed key; their bespoke schemas, lazy ensures, and per-table accessors
collapse onto the shared config_machine_state KV under namespaced keys.
cron_store_epochs retires outright: it was born write-only in #114388
and no reader ever existed in any language. Durable values (update
check state, voicewake triggers and routing, per-workspace onboarding
answers) migrate insert-if-absent during the v10->v11 migration; cache
class contents rebuild on next use. Deferred with named reasons:
exec_approvals_config (macOS direct-SQL contract), installed_plugin_index
(same-tx lease fence), node_host_config and web_push_vapid_keys
(secret-table git-backup redaction).

# Conflicts:
#	src/skills/workshop/collection-review-state.ts
#	src/skills/workshop/collection-review.gateway-admission.test.ts

* test: register v11 guard carve-outs and suppression pin

The v11 migration module joins the raw-SQLite allowlist (migrations are
the named guardrail exception), the lint-suppression allowlist records
the second type-parameter suppression in config-machine-state, and the
identity module keeps only externally consumed exports.

* test: surface CLI stderr when migration-diagnostic assertion fails

* test: expect migration diagnostics on stderr for models plain commands

The #129037 pending-migration cases asserted that aliases/fallbacks
lists never open the state database, but config-health observation
(observeConfigSnapshot -> readConfigHealthStateFromStore) full-opens it
on any config read whose file exists — reproduced identically on clean
main with a main-built dist. The protected contract is exact stdout;
the diagnostic legitimately lands on stderr for every case.

* test: drop unused defaults import from CLI stdout e2e

* test: split session path derivation out of oversized session-files suite

#130016 pushed session-files.test.ts to 1008 lines, over the 1000-line
lint cap and red for every PR's check-lint. The sessionPathForFile
describe moves to a self-contained sibling following the existing
session-files.*.test.ts split pattern; no assertions change.

* refactor(state): fold four more singleton tables into schema v12

tui_last_sessions (cache-class, regenerates on next session switch),
sidebar_sections (persistent section order, migrated as one JSON array),
node_host_config, and web_push_vapid_keys join the v12 fold-in, taking
the retirement to thirteen tables at the same version. The two secret
singletons were blocked on table-granular git-backup redaction; backups
now exclude config_machine_state rows by secret key prefix (nodeHost.*,
webPush.vapidKeys) with a fail-closed row filter and regression proof,
so STATE_SECRET_TABLE_NAMES sheds both tables. The sidebar fold also
retires its lazy-ensure WeakSet and inline DDL; sidebar edits stay
inside the existing session-group write transaction via direct Kysely.

* fix(node-host): omit absent Cloudflare Access config like the column reader

The KV rewrite returned gateway.cloudflareAccess as an own undefined
property where the retired column reader omitted the key; toStrictEqual
consumers (state-migrations doctor-repair test) caught the shape drift.
Mirror the column reader's conditional spread at both construction
sites.

* fix(backup): disclose redacted machine-state prefixes after restore

The prefix-granular secret redaction recorded omitted key prefixes in
the backup manifest but the restore result exposed only excludedTables,
so a redacted restore looked complete while nodeHost.* and
webPush.vapidKeys configuration were intentionally absent. The restore
result and CLI output now disclose the omitted prefixes (JSON mode
carries them via the result shape), with restore-side regression
coverage.

* fix(tui): compare-and-delete retired session pointers

Doctor cleanup read matching pointer keys then deleted them
unconditionally, so a replacement pointer written between the scan and
the delete was erased. The delete now re-checks the stored value inside
the write transaction and only removes pointers that still name a
retired session; a live replacement survives (regression covered).
Also corrects the stale schema-version line in database-first.md.
2026-08-26 13:32:19 -07:00
Josh Lehman 3b78d72431 fix(inbound-meta): avoid Anthropic billing classification (#130298) 2026-08-26 13:30:18 -07:00
Peter Steinberger fc2724d831 fix(release): normalize package tarball modes and prove non-root install (#130335)
npm/pnpm pack copy on-disk file modes into the tarball, and node-tar's
portable mode-fix only strips group/other write bits — it never adds
read bits. A restrictive-umask build host therefore ships owner-only
(0600/0700) tarball entries, which breaks the CLI for non-root users
after `sudo npm install -g` under mode-preserving consumers such as
system tar.

- Normalize every packed entry to 0644/0755 (a+rX, exec bits kept) as
  the last step of packOpenClawPackageForDocker.
- Add a tar -tvf mode gate to check-openclaw-package-tarball that
  rejects any non-world-readable entry.
- Run the docker-package-install npm lane as root and execute the
  installed CLI as a non-root user to prove the fix live.
- Fix the docker-package-install bun proof, broken on main since
  #129552 wired the bun smoke into the shared openclaw-e2e-instance
  library: replace the drift-prone per-file harness copy list with
  directory copies, and add a closure-walking guard test that fails
  on missing harness dependencies.
2026-08-26 13:24:13 -07:00
Heming Zeng 2555e9fa4e fix: stop retry storms after definitive channel rejections (#127353)
* fix(outbound): terminalize definitive channel rejections

* refactor(outbound): rethrow unclassified Slack rejections by identity

The Slack send boundary replaced every non-Error rejection with a synthetic
Error before classifying. That changed the propagated value at all four send
call sites, contradicting the stated contract that unlisted rejections keep
their previous path, and forced a compensating one-level `cause` walk in
`isSlackInvalidBlocksError` so the downstream `invalid_blocks` fallback could
still match. The guard's second operand was also dead: `isRecord` accepts Error
instances, so `!(err instanceof Error) || !isRecord(err)` never reached its
right side for a plain object.

Classify off the raw value and rethrow unclassified rejections by identity;
the `cause`-walking compensator and its test go away with it. Distill the
Telegram migration classifier's three-state result object into a
message-or-nothing, and document the description-first and 52-bit id contracts
inline.

Production surface for the PR drops from +71/-11 to +62/-8.

* test(agents): remove clock-tick race from workspace bootstrap ctime coverage

The in-place-edit case added in #127769 assumes restoring mtime leaves ctime as
the only changed stat field, and that it therefore differs. Linux and macOS
stamp ctime from a coarse per-tick clock, so an edit landing in the same tick as
the cached stat leaves ctimeMs equal: the ctime-only scenario never occurs, the
cache correctly serves its entry, and the assertion fails. Measured 199/200
identical ctimes in a tight loop; the test failed 1 in 8 local runs and broke
checks-node-compact-large-14 on CI.

Re-touch until the kernel advances ctimeMs, then assert mtimeMs and size are
unchanged so the scenario is provably ctime-only. Stripping ctimeMs from the
cache identity still fails the test.

* fix(slack): keep post-dispatch upload completion rejections ambiguous

PlatformMessageNotDispatchedError is a provider assertion that no
recipient-visible send began, and its contract says never use it after an
ambiguous send. files.completeUploadExternal runs after onPlatformSendDispatch
and is the one-time share operation, so a rejection there cannot prove the file
was never shared however definitive its code reads.

Drop the permanent-rejection classifier from that call and keep it on the
pre-dispatch calls only (chat.postMessage, files.getUploadURLExternal,
resolveChannelId). The upload test that pinned the old behavior asserted
onPlatformSendDispatch had already fired, which is exactly the condition that
forbids the claim; it now pins ambiguity instead.

Also widens the workspace bootstrap ctime wait to a 1s deadline and reshapes it
as a while loop, so a coarser filesystem tick cannot exhaust the bound.

* test(slack): prove permanent rejection recovery

* fix(test): stabilize Slack channel action routing

* fix(test): retain channel parity for precise targets

* refactor(outbound): drop unrelated test-routing changes

* fix(telegram): require Bot API error code for migration rejection

* test: repair Telegram tuples and preserve Slack test routing

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 13:16:48 -07:00
Peter Steinberger 1560142754 fix(e2e): stable candidate plugin repair fails after update (#130306)
* fix(e2e): resolve stable candidate plugins by latest

Amp-Thread-ID: https://ampcode.com/threads/T-01a039ee-f7c4-71d8-8b71-e028dbd60493

* fix(e2e): preserve stable fixture dist-tags

Amp-Thread-ID: https://ampcode.com/threads/T-01a039ee-f7c4-71d8-8b71-e028dbd60493

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 13:08:37 -07:00
Peter Steinberger e1a9f086e4 refactor(channels): compact generated channel catalog (#130284) 2026-08-26 12:19:31 -07:00
Peter Steinberger 21df0f36a4 fix(e2e): mount prerelease registry verifier (#130288)
Amp-Thread-ID: https://ampcode.com/threads/T-01a039ee-f7c4-71d8-8b71-e028dbd60493

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 11:58:43 -07:00
Peter Steinberger 50e4655ba6 refactor(providers): remove redundant private runtime facades (#130212)
* refactor(providers): remove redundant private runtime facades

* fix(xai): narrow x-search enabled config
2026-08-26 11:33:22 -07:00
Harjoth Khara 3be7baa1a3 fix(macos): timestamp hash-pinned Developer ID signatures (#118989)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 10:39:41 -07:00
Peter Steinberger a9e1b318d3 fix(workers): preserve remote turns through drain and cancellation (#130235) 2026-08-26 10:33:20 -07:00
Dallin Romney 67a310b2c6 fix(ci): publish maturity scorecards when main advances (#129955)
* fix(ci): retry stale maturity scorecard runs

* fix(ci): refreeze queued scorecard successors

* fix(ci): allow scorecard publication after main advances
2026-08-26 10:03:37 -07:00
Dallin Romney 8ce8085e84 fix(e2e): include Codex thread project id (#130086) 2026-08-26 09:59:36 -07:00
Dallin Romney d8ade72867 fix(qa): recognize Code Mode recovery in OTel proof (#129806)
* fix(qa): recognize Code Mode recovery in OTel proof

* test(qa): retain Code Mode recovery boundaries
2026-08-26 09:51:52 -07:00
Josh Avant 424521a3d3 fix(file-transfer): keep remembered approvals exact (#129762)
* fix(file-transfer): bind standing approvals exactly

* fix(file-transfer): keep validator error lint-safe

* fix(file-transfer): bind directory listing target before I/O

* fix(file-transfer): keep dir list preflight type stable

* test(file-transfer): split directory listing policy coverage

* fix(file-transfer): bind final node effects

* fix(file-transfer): close migration and archive gaps

* fix(file-transfer): bind standing grants before preflight

* fix(file-transfer): bind directory listings to checked target

* fix(file-transfer): bind final effects to filesystem identity

* test(file-transfer): prove exact approval transport

* fix(file-transfer): keep reapproval prompts path-scoped

* chore(config): refresh generated baselines
2026-08-26 09:38:50 -07:00
Dallin Romney c8c6e5a990 fix(qa): align repeated-request proof with provider timeout (#129851) 2026-08-26 09:34:48 -07:00
Miorbnli 20eef858aa fix(infra): prevent $-pattern injection in home directory tilde expansion (#122991)
* fix(infra): prevent dollar-pattern injection in home dir tilde expansion

String.replace interprets dollar-amp/dollar-1/dollar-backtick in the
replacement string. When the home directory contains these sequences
(e.g. a username with a dollar sign), tilde expansion via
.replace(/^~/, fallbackHome) corrupts the path silently.

Use a function replacement so the home path is treated literally,
matching the pattern already fixed in terminal-core/display-string (#111398).
Two call sites: resolveRawHomeDir and expandHomePrefix.

* fix(daemon): prevent dollar-pattern injection in state dir tilde expansion

Address review rank-up: the daemon state-path expansion deliberately does
not use the core helper and still passed home as a string replacement.
Apply the callback form here too and add a literal-dollar regression to
the existing service-env suite (fails on the string form, passes with
the callback).

* fix(launcher): keep literal $ patterns when expanding tilde OPENCLAW_HOME

* fix(ui): keep literal $ patterns in local media tilde expansion

* test(ui): prove literal-$ tilde local media preview through Control UI e2e

* test(ui): align literal-dollar media proof with compact attachment contract

Preserve the current authenticated metadata and ticket-scoped download behavior while exercising the real Chromium Control UI under a literal-dollar home.

Co-authored-by: liyuanbin <li.yuanbin1@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 08:24:10 -07:00
Peter Steinberger ef95d8f55e feat(secrets): agent-requested credentials the model never sees (#129670)
* feat(secrets): agent-requested credentials the model never sees

The new main-session secrets tool lets the agent request a credential by
name: the human enters the value in a masked question card (Control UI,
/ask/<id> deep link, iOS/macOS/Android), and the gateway diverts the
answer straight into the shared secret store at question.resolve. The
record, broadcast, waitAnswer, tool result, transcript, and model context
only ever carry a synthetic stored marker.

- protocol: additive secretStore binding, secretStoreExisting replacement
  metadata, and resolve-time secretStoreAllowedHosts (since 2026.8)
- gateway: store-bound question validation, admin-gated minting (blocks
  questions-scope self-answer escalation past secrets.store.set), shared
  redaction-first store write service reused by secrets.store.set
- tool: secrets request/list/delete; write-only by design, delete carries
  verified agent runtime identity; channel delivery is link-only so chat
  text is never captured as a secret
- Control UI: masked composer card with requester identity, store banner,
  editable allowed hosts, replacement warning, retry-on-validation-error,
  a standalone /ask/<id> page, and a named startup-JS baseline bump
- mobile: SecureField / password transformation for isSecret questions,
  no answer echo in terminal summaries; new native string registered in
  the locale-refresh inventory (generated artifacts stay workflow-owned)
- regression: claimed harness secret input stays out of session transcripts

Live-proven on an isolated dev gateway: real model turn, masked entry via
Playwright, value present only in secret_store_entries, absent from every
transcript, log, and the DOM.

* chore(protocol): regenerate protocol models and tool display

* fix(cli): read image string options through a typed helper

PR #129463 added four commander option narrowings in image.ts without
SAFETY coverage, leaving the assertion-safety ratchet red (21 > 17) for
every branch on current main. Replace the casts with a typeof-checked
read so the assertions are removed rather than annotated; each value is
still validated by its normalizer. SAFETY comments cannot work in this
file: the ratchet's raw scanner never rescans template tokens, so
comments after the first substitution template are unreadable to it.

* chore(protocol): refresh Swift models against current main

* chore(i18n): re-baseline the native inventory on current main

* docs(secrets): state the default-on tool policy and how to disable it

* fix(secrets): tell the model what the store actually does

The shipped tool description named the three actions and nothing else,
and no parameter carried a description. The model could not tell that
request blocks a human, that reason is shown to that human, what secret
and env select, or - the silent-failure case - that a secret stored with
no allowedHosts can never be substituted, so a successful request could
produce a permanently unusable credential. Move the description to the
presets module beside ask_user and document every parameter.

* refactor(agents): share one blocking-question lifecycle between tools

ask_user and secrets each carried their own registration, wait, and
cancel logic, and they had diverged: ask_user recovers an answer that
lands between its wait timeout and the cancel, while secrets discarded
it and reported no_answer even though the Gateway had already stored the
credential. One shared canceller and answer reader fixes that race for
both, folds the two divergent gateway-call types into one, and drops two
type assertions in favour of the canonical record guard (ask_user's
assertion baseline shrinks 11 -> 8).

Net +49 production lines: the shared module costs more than the
duplication it removes, and buys the correctness fix plus a single owner
for question lifecycle.

* fix(ui): keep the allowed-hosts field readable as an input

Main's composer restructure moved the free-text input styling into the
option-row context, so the store-request hosts field - which sits outside
a row - lost its border and read as static text. It is the one field the
operator is meant to review and edit before releasing a credential, so
give it its own border and focus ring.

* fix(secrets): close two credential-boundary holes in agent requests

Requests are now protected-secret only. list renders env values, so an
agent could request kind=env, watch a human type it into a masked box
under a no-visibility promise, then read it straight back; the tool text
even claimed values are never returned. Environment values stay operator
-set in Settings or the CLI, where they are agent-readable by design.

Store-bound questions are also bound to the run that requested them. The
resolve path authorized only the answering client, so a terminated or
replaced agent run could still have a credential written on its behalf -
the recorded runId was provenance, not closure-bound authority. Minting
now requires a runId and resolution revalidates that exact live run
immediately before the store write, with no await in between, failing
closed as QUESTION_REQUESTER_INACTIVE.

Both reported by ClawSweeper as P1 credential-boundary findings.
2026-08-26 08:10:16 -07:00
Peter Steinberger eb17c8da5a refactor(matrix): remove obsolete private runtime forwarding layers (#130129)
* refactor(matrix): remove obsolete private runtime forwarding layers

* test(commands): preserve filesystem safety exports in delete mocks
2026-08-26 07:33:52 -07:00
Peter Steinberger 3b22a6cdd3 fix(gateway): keep loaded conversations responsive under heavy load (#130071)
* fix(gateway): keep loaded sessions responsive under heavy load

* test(gateway): narrow concurrency lane and stabilize inherited lint

* fix(ui): retain presented snapshots during background prefetch

* chore: respect release-owned root changelog policy
2026-08-26 05:42:01 -07:00
Peter Steinberger f649017122 fix(test): drain late mock registrations (#126037)
Amp-Thread-ID: https://ampcode.com/threads/T-01a03d6a-d790-77bf-852b-65c57440ac84

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 03:32:21 -07:00
Peter Steinberger a842ad35f3 test(release): require live channel after restart (#127013)
* test(release): require live channel after restart

* test(release): migrate upgrade channel assertion

* test(release): assert channel runtime after restart

* test(release): await restarted channel runtime

* test(release): prove ClickClack reconnect generation

* test(release): preserve fixture runtime status

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-26 03:27:23 -07:00
xingzhou ee2f5f2084 fix(install): --json preserves valid NDJSON for dynamic values (#128682)
* fix(install): preserve valid NDJSON for dynamic values

* fix(install): satisfy installer shell checks
2026-08-26 03:19:43 -07:00
xydt-tanshanshan 2d6e0a23af [AI] docs(tools): clarify per-action required params in gateway and process tool schemas (#114879)
Update gateway path and process sessionId schema descriptions so models know
which actions require them at runtime. Includes regenerated Codex prompt
snapshots (base telegram catalog + md token counts) matching the descriptions.

Fixes #
2026-08-26 02:54:42 -07:00
Vincent Koc ce54cae86d fix(ci): stop source-only PR fallback requiring generated locales (#120973)
* fix(ci): scope native i18n parity for release gates

Punchcard-Session: amber-workshop-workshop-36

* fix(ci): preserve native locale scope for PR release gates

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 02:50:48 -07:00
Vincent Koc 02c5190014 fix(release): preserve beta channel in prerelease survivor (#129966)
* fix(release): preserve beta channel in prerelease survivor

* fix(release): register prerelease survivor scenario
2026-08-26 17:47:16 +08:00
Ayaan Zaidi a901d33184 fix(skills): share the foreground prompt prefix with the experience review (#130013)
The detached Skill Workshop experience review rebuilt its system prompt and tool catalog from a different context than the foreground turn, so every review missed the prompt cache. Native harnesses (embedded, Codex, Copilot) now hand the review the same foreground prompt context via buildEmbeddedForegroundPromptContext; the review reuses the foreground prefix and gates execution to skill_workshop while keeping the catalog identical. Reviews without a foreground prompt (CLI hook contexts) are skipped.
2026-08-26 09:39:54 +00:00
Peter Steinberger 1605dbd3ef chore(deps): refresh dependencies after seven-day cooldown (#129941)
* chore(deps): refresh dependencies after cooldown

* chore(deps): refresh sherpa-onnx runtime to v1.13.6

* test(ios): refresh Fastlane pin expectations
2026-08-26 01:37:37 -07:00
Peter Steinberger 3316bfd5ab fix(agents): keep automation tool guidance consistent (#129953) 2026-08-26 01:36:13 -07:00
Yiğit ERDOĞAN 2421de090c fix(agents): transient final-call failures discard a completed tool turn (#128840)
* fix(agents): transient final-call failures discard a completed tool turn

Provider-failure recovery only proceeds when the attempt carries
settledTurnFinalizationContext, and nothing on the embedded path ever
produced it, so the isolated tool-free finalizer could not run and a
settled post-tool turn was discarded whole when its final delivery call
hit a transient socket error.

Populate the context at the attempt-result owner, mirroring the codex
app-server producer for the same field: capture only on a failed
terminal, only when no assistant text was produced, and only when the
snapshot holds a settled tool result. Existing settlement, delivery and
async-work gates are unchanged.

* test(vitest): route the settled-turn finalization suite to its owner project

* fix(agents): reject observed timeouts before settled-turn recovery

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

* fix(agents): restrict settled-turn recovery to transient network failures

Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 01:15:50 -07:00
Harjoth Khara 7c6040550a ci(macos): budget macos-swift by runner so fork PRs finish (#119017)
* ci(macos): budget macos-swift by runner so fork PRs finish

runs-on falls back to hosted macos-26 for several cases: workflow_dispatch,
run_attempt > 1, fork pull requests, and (as of the runner-backend circuit
breaker landed on main the same day this PR was authored) breaker-routed
GitHub-hosted fallback. timeout-minutes did not cover the fork-PR case, so
fork PRs ran on the slow hosted runner with the Blacksmith-tuned 20-minute
budget.

Measured on PR #118989 (a fork PR): macos-swift was cancelled after 20m25s,
killed mid-compile at step 1365/1416. No test executed, and the log has no
swift compile error - only 'The operation was canceled.' ci-gate then fails
because it lists macos-swift as required, so the PR reads red for a reason
unrelated to its diff, and a contributor cannot rerun it.

Make the budget follow the runner instead of the trigger: every hosted path
gets 30 (folded into the same budget the circuit breaker's own hosted-fallback
timeout extension already established for this job), so this doesn't
reintroduce a second, competing hosted-timeout value. Blacksmith paths keep
20 unchanged. Replaces the single pinned-string guard with a table-driven
test covering runs-on and timeout-minutes together across every trigger
context that can route to a hosted runner.

* chore: refresh PR head (keep open for maintainer review)

* ci(macos): track main's author-association runner routing

Main now routes macos-swift by pull_request author_association rather than
fork-ness, so the timeout predicate and its guard scenarios follow it.

* ci(macos): tighten hosted runner budget coverage

Co-authored-by: harjoth <harjoth.khara@gmail.com>

* docs(ci): document hosted macOS budgets without merge conflicts

Co-authored-by: harjoth <harjoth.khara@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 01:12:07 -07:00
Peter Steinberger 4bd8859126 fix(skills): record skill usage again and retire dead curator tables (#129769)
* fix(scripts): see SAFETY comments after template substitutions

The assertion ratchet scanned each file with a raw ts.createScanner, which
reads the `}` closing a template-literal substitution as a block close. The
scanner desynced there, so every `// SAFETY:` comment after a file's first
`${...}` was invisible and its annotated assertions were counted as bare.

Track substitution brace depth and rescan the closing brace the way the parser
does. The fix uncovers already-annotated assertions in eight files, so the
baseline shrinks accordingly.

* fix(skills): record skill usage again and retire dead curator tables

Skill lifecycle curation shipped in 2026.7.1 with two producers: a trusted
`skill.used` consumer writing `skill_usage`, and a daily sweep aging skills
into `skill_lifecycle`. The weekly collection review (#121653) replaced that
policy and deleted both producers, but left every reader in place. Since then
`skill_usage` has had no writer, so curator status reported `lastUsedAtMs:
null` and `useCount: 0` forever, and `skill_lifecycle` had no writer either, so
pin/unpin/restore either threw "not found" on fresh installs or, worse,
"succeeded" on upgraded ones while gating nothing at all.
`skill_workshop_proposal_origin_runs` was never read in any revision; proposal
provenance is authoritative in `record_json`.

Restore the usage producer at its owner and retire what has no owner:

- `skill.used` events populate `skill_usage` again, registered with the
  collection-review maintenance it belongs beside. Curator status derives
  curated skills from applied Workshop create proposals and reports real usage.
- Weekly review receives bounded `useCount` / `lastUsedDaysAgo` evidence, with
  prompt text stating usage supports keeping a procedure and never alone
  justifies a drop.
- State schema 10 drops `skill_lifecycle` and
  `skill_workshop_proposal_origin_runs`. Previously archived skills return to
  the active collection, where review judges them by content; the migration
  logs how many. Reconcile now clears usage only for skills it actually drops.
- `skills.curator.pin`/`unpin`/`restore` stay registered for existing clients
  but fail with an explicit retirement message instead of silently doing
  nothing.

Retirement code moves to openclaw-state-db-table-retirements.ts to keep the
schema-repair module under max-lines; that split is a pure relocation.

Production delta is +23 raw: about -46 for the change itself, +44 for the file
split, +25 for the ratchet fix in the previous commit.
2026-08-26 01:06:06 -07:00
Peter Steinberger 079f8190ed fix(workers): keep remote execution on its selected node (#129929)
* fix(workers): preserve remote execution authority and cleanup

* test(workers): narrow optional gateway request payloads

* test(workers): extract reusable tunnel fixtures
2026-08-26 00:36:49 -07:00
Peter Steinberger 10108ca0f2 fix(e2e): suppress update checks inside Docker E2E containers (#129931)
* fix(e2e): suppress update checks inside Docker E2E containers

The runner's CI variable does not cross into `docker run`, so containers kept
reporting daily update checks and drowned real operators in the telemetry
aggregates. Inject the existing suppression switch from the shared helper so
every lane inherits it; callers that exercise update behavior keep their own
value.

* test(e2e): record the injected suppression in docker run contracts
2026-08-26 00:14:40 -07:00
Peter Steinberger 6d1c1c9f57 refactor(cron): unify heartbeat automations under cron ownership (#129862)
* refactor(cron): unify heartbeat automations under cron ownership

* test(cron): align heartbeat automation fixtures and snapshots

* test(heartbeat): preserve rebased manual-wake coverage

* test(ui): scope mobile session actions to active chat pane
2026-08-26 00:08:18 -07:00
Peter Steinberger 0229fe7a2a improve: verify packaged runtime on Bun 1.4 (#129552)
* test: exercise packaged OpenClaw under Bun

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-66db-71f0-91e7-1578b383afb2

* docs: align Bun runtime guidance

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b7-66db-71f0-91e7-1578b383afb2

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-25 23:41:05 -07:00
Peter Steinberger b4c936ea82 fix(cli): preserve human output for flag-like model option values (#129911)
* fix(cli): preserve human output for flag-like model option values

* test(cli): isolate model output preaction regressions
2026-08-25 23:31:52 -07:00
Peter Steinberger 2e50bdf9fb test(release): route workflow regressions (#129914) 2026-08-25 23:21:59 -07:00
Josh Avant eedc554f6e fix(webhooks): keep TaskFlow child actions within the owning session (#129633)
* fix(webhooks): bind TaskFlow child actions to owning runs

* fix(tasks): preserve task flow import boundaries

* test(tasks): cover exact-run cancellation callers

* test(webhooks): prove ACP replacement cancellation fence

* fix(tasks): preserve authority across lifecycle races

* fix(tasks): bind TaskFlow children to runtime instances

* fix(acp): revalidate queued cancellation authority
2026-08-25 23:13:50 -07:00
Peter Steinberger 0285926bf9 refactor(codex): compact generated protocol JSON (#129524) 2026-08-25 22:26:54 -07:00
Vincent Koc 263d2a2a91 fix(i18n): include concatenated Apple UI strings (#129882)
* fix(i18n): include concatenated Apple UI strings

* test(i18n): cover multiline Apple modifiers
2026-08-26 13:24:48 +08:00
Vincent Koc 67c5a85619 fix(release): bind prerelease plugins into package Telegram QA (#129784)
* fix(release): bind prerelease plugins into package Telegram QA

* fix(release): preserve registry artifact provenance
2026-08-26 13:17:30 +08:00
Peter Steinberger eacceebe3e fix(cli): preserve populated model output on stdout in plain mode (#129871) 2026-08-25 21:54:08 -07:00
Vincent Koc 2b06180bb5 fix(package): restore npm package size headroom (#129782)
* fix(package): externalize diffs build outputs

* chore: remove release-owned changelog entry

* fix(build): generate selected plugin assets before Docker staging

---------

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-08-26 12:40:27 +08:00
Peter Steinberger a5f36aef39 fix(parallels): avoid host networking for macOS upgrades (#129861) 2026-08-25 21:37:00 -07:00
Peter Steinberger 3a37a12d89 fix(package): prevent installs from deleting validated runtime assets (#129724)
* fix(package): enforce postinstall inventory parity

Reject packages whose pruning inventory omits shipped dist assets, and verify every installed bundled plugin artifact survives postinstall. Preserve lifecycle-free historical package compatibility.

Fixes #129722

* fix(package): keep parity helper private

* refactor(package): deduplicate SDK artifact requirements
2026-08-25 21:14:39 -07:00
Peter Steinberger 6f1ed5396e fix(release): start approved VCR mirror recovery jobs (#129829) 2026-08-25 21:11:18 -07:00
Vincent Koc db085b7d8f fix(tooling): arm prompt cleanup before spawn (#129755) 2026-08-26 11:43:39 +08:00
Peter Steinberger c406ef4b4d test: consolidate prompt snapshot deltas (#129295) 2026-08-25 20:42:03 -07:00
Peter Steinberger 20b453f155 fix(agents): reject forged plugin owner authority (#104872)
* fix(security): bind channel owner authority to trusted runtime

* fix(plugins): preserve untrusted channel guest ingress

* fix(plugins): snapshot channel ingress authority once
2026-08-25 20:36:05 -07:00
Peter Steinberger e82700626d fix(ui): serialize remote session permissions before sending (#129674)
* fix(ui): serialize remote session permissions before sending

* test(ui): await mobile side-panel close before progress assertion
2026-08-25 20:35:09 -07:00