* refactor(runtime): probe SQLite startup capabilities in a worker
Move current-runtime capability SQL off the main thread and share one pending result across launcher and runtime callers. Await worker exit before admission, preserve failure caching and Bun library selection, and retain the native SQLite SDK and foreign-runtime probe contracts.
* test(config): expose include provenance in writer failures
Carry the bounded consumed-snapshot, provenance, and include-boundary assertions from 3de18f60bd (#143587). Preserve the observed snapshot and all existing writer/exclusion assertions. The hosted failure cause remains unresolved.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Use the shared activation merge at the early startup handoff, retaining materialized settings without changing source configuration. Cover sparse provider overlays and real cold startup through a live configuration reload.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(state): move session delivery queue operations to shared worker
Run standalone queue persistence on the existing shared-state worker. Carry captured database context through producers, recovery, generated media, and retry scheduling, and join admitted scheduler work before closure. Preserve queue JSON, deduplication, retry, and settlement contracts.
Retain compound task/subagent transactions and current main Health, diagnostics, and task/flow behavior. Route real-worker fixtures through their canonical fork owners and remove obsolete test-only queue facades.
Validation: fresh P2 review, full 36-command changed gate, 19 selected routing tests, and five ordinary queue/Health/media/settlement cases passed.
* test: preserve leaf ownership in helper routing checks
Normalize the dedicated Gateway worker config when comparing singleton file selection with a mixed Gateway aggregate. Keep importer completeness, exact-once membership, includes, forwarded arguments, and watch-mode assertions unchanged.
The original case reproduced the hosted Large 15 failure. The corrected case, fresh P2 review, and all 16 selected changed checks pass. Runtime sources and compiled artifacts remain identical to the qualified parent.
Keep Control UI PR badges and publication progress aligned with scoped GitHub metadata and recorded Gateway outcomes. Separate status reads from publication, recover shared receipts after reconnect, and preserve explicit personal confirmation and unknown-outcome fences without another polling loop.
Retain unbound legacy terminal receipts as history without blocking account choices or inventing lifecycle bindings. Cover retained-state upgrades and complete the qualified-owner options test contract.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Separate package metadata from runtime directory resolution so settings helpers avoid unnecessary startup work. Preserve package values, asset lookup, and the supported session SDK exports.
Pass the explicit environment to all five ordinary registry writers and
use the connection supplied by the shared transaction owner. Previously
they could transact on the ambient database while writing another handle,
so an unrelated read-only state blocked valid writes and a failed worktree
deletion could leave its snapshot chunks deleted.
Tiny two-state regressions reproduce both failures and now pass, alongside
existing registry and focused snapshot/HEAD-change preservation tests.
Keep page-owned reader intent authoritative for geometric end anchoring, automatic end commands, and typing follow. Preserve explicit Latest/manual returns and standalone transcript behavior. Cover resize-clamped readers with short and long live answers, both motion modes, and existing end-follow contracts.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reuse one install-choice catalog for deprecated and selected-choice lookup,
and retain only the final stable catalog sort. Preserve trust filters,
blank/rejected paths, deprecated precedence, and choice ordering.
Validate 45 owner tests, the commands/plugins-platform type graphs, and
actual-owner catalog/result/trace parity across the bounded synthetic corpus.
Preserve the causal schema-preflight diagnostic instead of its generic npm
bypass footer. Check package disk space before registry and schema work and
retain low-space warnings in update history without introducing a hard stop.
Show pending, successful, failed, and disconnected status checks in the update
dialog. Only acknowledge refresh responses that the controller actually applies.
## What Problem This Solves
CLI health diagnostics silently treat every failed directory metadata lookup as an absent directory. A cyclic project-directory symlink, an inaccessible workspace, or a file blocking a parent component can consequently produce no directory warning.
## Why This Change Was Made
Keep only `ENOENT` on the existing quiet missing-directory path. Other `statSync` failures use the existing unreadable-directory diagnostic and repair guidance, through the shared errno helper. Successful regular-file and read/write checks remain unchanged.
This is independent of the configuration-root work in #145895: it does not change directory selection, authentication, backend registration, transcript lookup, or persistence.
## User Impact
Operators receive a directory warning instead of silence when the configured CLI workspace/project metadata cannot be inspected. Healthy directories and genuinely absent first-use directories remain quiet. Doctor does not create, repair, remove, or change permissions on any directory in this patch; update execution and rollback behavior are unchanged.
## Evidence
### Historical September 14 integration refresh
Refresh head: `1e4d36ce602446a2821aea6fa2e4be1a17a17f9e`. Original branch head `4b256621d378b969f52c3c7fb1aa1479df656384` and pinned main `8b917bc499` are both preserved as parents; no force-push. Refresh the existing accepted patch onto pinned main to remove stale integration inputs and obtain new-head CI. No new feature or unrelated failure workaround is added. The resulting tree exactly matches the conflict-free git merge-tree result; there are no manual source or test edits.
Validation on Linux / Node 26.1.0 with frozen-lockfile dependencies: No new local runtime test is claimed for this mechanical integration; existing proof keeps its original revision and exact-head hosted CI is required. The native staged-content/format guard, main-relative diff check, and one fresh independent P0-P2 source review passed. No full build or full type-aware check was rerun locally; new-head CI is tracked separately. Earlier runtime proof below retains its recorded SHA; it is not relabeled as a new execution.
Candidate: `4b256621d378b969f52c3c7fb1aa1479df656384`, based on `e9c1cb7e71`. Linux, Node 26.1.0; isolated synthetic HOME/config/state and a physically independent dependency installation. The production file was rechecked against current main `0e4b4d1fa3`; its metadata catch still has the same defect.
### Observed directory diagnostics
The source-runtime driver imports the actual `<directory-health-owner>` entry and project-directory resolver through `scripts/tsx.mjs`. It creates a real cyclic project symlink (`ELOOP`) and a real file-parent/child path (`ENOTDIR`), captures the entry's note output, and checks real filesystem contents afterwards. No filesystem result or directory-health function is mocked. Only the external CLI executable is a synthetic executable returning a logged-in status; the production auth subprocess launches it normally.
```sh
node --import ./scripts/tsx.mjs <proof-driver> "$PWD" baseline
node --import ./scripts/tsx.mjs <proof-driver> "$PWD" candidate
```
| Same native filesystem scenario | Before | After |
| --- | --- | --- |
| Cyclic project-directory symlink | No note | Project directory is not readable; existing repair hint |
| Workspace below a regular-file parent | No note | Workspace is not readable; existing repair hint |
| Healthy directory | No note | No note |
| Missing workspace | No note | No note |
Actual candidate output, with temporary paths redacted:
```text
- <backend> project dir: <isolated-home>/.<backend>/projects/<workspace-key> is not readable by this user.
- Fix: make the <backend> project dir readable, or remove the broken path and let <backend> recreate it.
- Workspace: <fixture>/file-parent/child is not readable by this user.
- Fix: make the workspace a readable, writable directory for the gateway user.
```
Both driver runs recorded exactly four `auth status --json` fixture invocations, unchanged configuration/executable/blocking-file bytes, and successful scratch removal. Candidate production SHA-256 `c3eac4e054ffdd415b4b35fd3e55aee3e492a25bcde62ecc0beb26ffd8ff2075` remained unchanged during proof and matches the committed file.
The unbuilt source checkout emitted bundled backend setup-entry loading warnings and used the unchanged default CLI-command fallback. This is directory-owner proof with a real subprocess fixture, not verification of bundled backend discovery, installed Doctor bootstrap, a real account, an updater, or model execution. The warnings were retained rather than counted as successful plugin loading.
### Historical repository validation
- Baseline existing-suite run: 9 passed / 5 failed. Four metadata-error cases observed zero notes, and the real blocked-parent case also observed no note. The candidate uses the existing unreadable wording for an unresolvable child, without claiming the child exists.
- Candidate `node scripts/run-vitest.mjs src/commands/doctor-<backend>-cli.test.ts --reporter=json --outputFile=<receipt>`: **14/14 passed**. Existing tests are retained; new cases cover EACCES, EPERM, EIO, ELOOP, actual ENOTDIR and a missing-path control.
- `node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.core.json src/commands/doctor-<backend>-cli.ts src/commands/doctor-<backend>-cli.test.ts`: **2 files, 263 rules, zero warnings/errors**.
- Changed-file `oxfmt` and the normal formatting commit hook passed without changing the proof-covered production bytes.
- One fresh independent P0–P2 autoreview of the staged diff: scoped-clean, no actionable findings. The reviewer did not rerun tests or builds.
The historical execution above does not claim a full build, full typecheck, Windows execution, published-updater test or all-green hosted CI. There are no new configuration keys, public APIs, stored formats, log policies or dependencies. The production change is confined to classifying an existing diagnostic failure.
---
## Current validation at 937d7f2a
The contributor commits and refresh remain intact. One additional commit replaces the helper-only regression cases with registered Doctor dispatch tests. The historical results above retain their original identities; the following evidence describes the current head. Backend names and local proof locations are generalized for publication.
## Problem and fix
Doctor could silently omit its directory-health warning for an unreadable or cyclic project directory. Only an absent path now stays quiet after a failed metadata lookup; other failures reach the existing warning and repair guidance.
## Impact
Readable and first-use paths stay quiet. Directory contents and permissions are unchanged. The contributor's production fix and authorship are retained; the correction changes only tests.
## Evidence
At `937d7f2a5e6c830782164c3453efea77c8556367`, isolated `pnpm openclaw doctor --non-interactive` runs deliver warnings and matching hints for native cyclic, permission-denied and blocked-parent paths. Readable/missing controls stay quiet; regular-file behavior is unchanged. The structured cyclic check returns one warning and exit 1.
Registered dispatch tests fail for both broken directories on main `8b917bc499` and pass on the correction. The owner plus three Doctor sibling files pass 213 tests. The required local type-aware preflight passes. Hosted CI run [34807143893](https://github.com/openclaw/openclaw/actions/runs/34807143893) passed at this exact head after one retry of a shutdown-timing test group. The same group passed locally on the head and plain main (172 tests).
Historical captures remain attributed separately: original candidate `4b256621`, historical merge `193a40f08164`, and their recorded main baselines. The merge's 214-test record was recovered without rerunning it; its real CLI matrix now has matching source/build identities.
## Consumers
Normal Doctor output and structured findings share the corrected directory-health owner. Directory selection, authentication and transcript readers are unchanged. Tests exercise registered dispatch, runtime selection and final warning delivery, with readable/missing controls.
AI-assisted.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Gateway startup no longer inspects each agent database one after another; installations with many agents start, and therefore finish updates, faster.
Bound agent database inspections to two while preserving the existing child-reader and private-snapshot owners, schema and ownership checks, deduplication, input-order results, and cleanup before failure or cancellation settles. Two fallback snapshots may coexist; the maintainer accepts that temporary-storage tradeoff.
Validation: serial overlap controls failed as expected; 125 focused tests passed. Changed-file checks, package build, and tarball integrity passed. Published 2026.9.3 to candidate and the forced second package update both completed with settled verification, managed Gateway restarts, and no transaction residue.
Refs #139870.
Thanks @XuZhi1982 (#139878).
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Remove two private forwarding wrappers and the obsolete type assertion
while preserving section omission, validation order and runtime identity.
Retire the matching assertion allowance.
Include the existing reclamation fixture prerequisites from
d4d3d6cda5 and
9c931371a1 with no production credit.
Validation: paired 28-case command coverage, the canonical reclamation
fixture, the full 31-check gate and a scoped-clean P2 precommit review.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(memory): retain worker session export retries
Reject after two obsolete redaction snapshots instead of exporting on the
Gateway thread. Record resolved targeted sessions before preparation so a
failed export retains pending work for generic retry. Full rebuilds continue
to preserve the published index.
Verified both regressions against independent controls and all four ordinary
redaction cases against the final code.
* test(memory): isolate queued retry recovery from dirty state
Use the existing maintenance handoff so ordinary dirty-file retries cannot rescue a dropped queued request. Preserve every queue recovery assertion.
## Contributor description — original implementation
The contributor’s original description follows. Historical heads, scope and open decisions in this section refer to that implementation; the reviewed current implementation, compatibility decision and proof are recorded in the maintainer update below.
Closes#133245.
Related: #127950. This supersedes the current-main-incompatible queue-owned approach in #125385 while preserving today's canonical timeout retry disposition.
## What Problem This Solves
A durably claimed channel message can wait behind another follow-up turn for longer than the five-minute claim-to-adoption watchdog. Existing liveness renewal covers queue-head active-run admission checks, but not an ingress-backed lifecycle waiting deeper in the follow-up queue. The claim can therefore be retired before the message reaches the model.
## Why This Change Was Made
The follow-up queue now owns periodic deferred heartbeats for the exact ingress lifecycle while it remains in pending items, in-flight delivery, summary sources, or compacted summary elisions.
Ingress supplies a cadence derived from one third of its adoption-stall timeout. Debounce, Plugin SDK fan-in, and channel lifecycle wrappers preserve the shortest applicable cadence. Queue ownership triggers an immediate renewal, then periodic renewal; it stops after successful adoption, completion, ownership loss, or callback failure. A rejected adoption callback keeps renewal alive for the supported retry path.
The existing watchdog and canonical timeout retry policy remain unchanged, so silent handlers and orphaned claims still recover normally.
## User Impact
Messages accepted by durable channel ingress remain adoptable while they wait behind long-running work instead of silently disappearing before model execution. No setting or migration is required.
## Evidence
SDK documentation polish (2026-09-10, head `76097fef9397a04c10637e06dfaf64ebb6ca104a`): the public channel SDK guide now documents forwarding both heartbeat fields, the shortest-positive-finite fan-in cadence, renewal termination, and compatibility when older wrappers omit the optional cadence. The documentation-only addition passed changed-page formatting, MDX sanity, and `git diff --check`; production code and the previously tested runtime head below are unchanged. Current-head hosted CI has completed successfully; the final exact-head ClawSweeper review accepts the implementation and proof, leaving only SDK-owner acceptance of the documented contract.
Refreshed on 2026-09-08 for exact head `57c4faff0ed7bb2d9b4fd308aa46b19ae4095e61`, rebased onto `1ff45cad5a`.
- Preserved upstream's ingress-monitor type extraction and gateway-suspension repair; the optional cadence field follows its new type owner.
- Repaired the retry integration fixture: after real enqueue and initial renewal, only the abandoned message's heartbeat callback fails. The real watchdog then recovers it. Retry delivery, healthy sibling cleanup, and true duplicate assertions remain intact; no production behavior or timeout changed for this repair.
- 102 focused tests pass: dispatch ingress retry, queue in-flight/dedupe, ingress lifecycle/watchdog, Plugin SDK fan-in, Discord queue handling, and Slack handler.
- Changed-file gates pass: core/core-test/extension typechecks, formatting, changed core/extension lint, SDK boundaries/exports, dead-export scans, and repository guards.
- Fresh independent source review found no actionable P0–P2 defects. The standalone autoreview CLI failed at startup without a verdict and is not counted as review coverage.
- Current-head CI run [34520397963](https://github.com/openclaw/openclaw/actions/runs/34520397963) completed successfully. The earlier startup subprocess timeout is historical, not a current failing gate. Exact-head ClawSweeper review (September 10, 20:00 UTC) reports no actionable correctness or proof findings; explicit SDK-owner acceptance remains required before merge.
- Existing regressions cover immediate renewal after late handoff, periodic deeper-queue renewal, stopping after owner loss, and continuing across a rejected adoption callback.
Exact-head durable-ingress boundary proof used isolated SQLite with the production ingress drain/lifecycle binder, follow-up queue, and canonical adoption helper. The model callback was synthetic; this is not live Telegram or Discord transport proof. No production Gateway, channel state, or configuration was touched. Temporary state and queue ownership were cleaned up.
```json
{
"schema": "openclaw.pr133248.durable-ingress-proof.v1",
"exactHead": "57c4faff0ed7bb2d9b4fd308aa46b19ae4095e61",
"setup": {
"durableStore": "isolated OpenClaw SQLite state",
"executor": "production follow-up queue with synthetic model callback",
"transportLifecycle": "production ingress drain lifecycle",
"adoptionStallTimeoutMs": 180
},
"queuedBehindLongTurn": {
"heldMs": 620,
"formerDeadlineCrossed": true,
"heartbeatCount": 11,
"claimStillOwnedAtCheckpoint": [
"queued-event"
],
"retryRowsAtCheckpoint": 0,
"failedRowsAtCheckpoint": 0,
"executionCount": 1,
"duplicateAfterAdoption": "completed"
},
"orphanRecovery": {
"status": "released-for-retry",
"attempts": 1,
"lastErrorContainsHandlerTimeout": true
},
"productionTouched": false
}
```
Owner acceptance:
- Intended behavior: accepted messages remain adoptable while their exact lifecycle is owned by the queue; ownerless claims retain canonical timeout recovery.
- Boundary: one optional cadence field propagated through existing shared lifecycle and channel wrappers; no configuration, schema, or migration change.
- Maintainer decision remains open: accept the additive public Plugin SDK lifecycle field and its queue-owned cadence semantics. Bot review is not that acceptance.
- Rollback: revert the renewal fix and its companion retry-fixture adjustment.
- Scope: 23 files, +273/-2. The width is required lifecycle forwarding; renewal policy stays in the queue and ingress drain.
AI-assisted.
---
## Maintainer update — reviewed head `9325ad501aa4`
## What Problem This Solves
Messages accepted while a long reply is running can expire in the follow-up queue and consume a retry. The reproduction confirmed expiry and retry; all three messages eventually arrived. It did not reproduce the older permanent-loss report in #133245.
## Fix and impact
The queue starts one heartbeat when it accepts a lifecycle and stops it on adoption, completion, cancellation, or callback failure. Heartbeats no longer scan queue collections or copy the in-flight set. Ingress remains responsible for the watchdog and retry settlement, and a heartbeat cannot undo the watchdog pause during adoption finalization.
Ingress derives the cadence from its adoption timeout. The optional lifecycle metadata remains necessary because wrappers rebuild callbacks and combine abort signals, losing the original timeout. No channel setting, schema, migration, dependency, or protocol change is added.
The simplification removes 110 lines net from the initial implementation plus main merge. Total production growth is 40 lines. Existing lifecycle types are reused, and the queue cases share the existing lifecycle test fixtures.
## Evidence
- Real Gateway and Discord: three marked messages on one route, with a queued reply held for 330 seconds. Main expired the third claim after 300.004 seconds. This head retained the same claim at 322.257 seconds with zero attempts; replies arrived once, in order, at 17.804, 349.201, and 350.223 seconds.
- Real SQLite/drain/binder/queue controls: explicit abandonment releases the claim; callback failure stops renewal and lets the watchdog retry without model execution.
- 191 core owner/sibling tests and 190 channel tests passed. All five new regression cases failed on plain main for the intended reasons.
- The local preflight passed core/extension type-aware lint, production and test types, script types, and protocol checks in an isolated checkout of this head.
- External consumers compiled and ran against published 2026.9.4 and this head, including omitted metadata, forwarding, optional return fields, and asynchronous abandonment.
## Consumers
Shared ingress binding, batching, reply dispatch, and the Feishu, Slack, Telegram, and Twitch wrappers preserve the source cadence. The fan-in uses the shortest valid cadence. Legacy wrappers that omit the optional field retain their existing head-only heartbeat behavior. Adoption, queue clearing, overflow, cancellation, summaries, and drain replacement retain or finish the same lifecycle owner.
Closes#133245.
Original implementation and report by @PollyBot13 (#133245). The contributor's commits and authorship are retained.
AI-assisted.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Closes#146939. Related: #140984.
## What Problem This Solves
Fixes `memory_get` returning a different file from `memory_search` when an explicitly owned multi-agent roster relies on inherited workspace paths.
## User Impact
Search results and subsequent reads agree on the agent's workspace without requiring a per-agent workspace pin. Explicit workspace overrides remain authoritative. No files move, no index reset or storage migration is required, and Markdown containment and read limits are unchanged.
For explicitly owned rosters, an unpinned first agent no longer implicitly reads the parent workspace merely because it appears first. A valid retained migration owner and sole-agent inheritance still follow canonical behavior. Raw reader inputs without explicit ownership keep their existing legacy behavior.
## Why This Change Was Made
The prior path repair in #140984 deliberately deferred workspace-ownership alignment. This PR proposes only that bounded alignment: core and explicitly owned memory-host reads share the compatibility-owner decision through a pure internal helper. The existing private path bridge avoids importing the full agent runtime. The original config object is preserved for retained migration-owner lookup; optional legacy reader IDs remain accepted.
### Maintainer choices — proposed, not previously approved
1. **Accept explicit-roster alignment (recommended and implemented).** Search's canonical workspace ownership is authoritative for reads too. Alternatively, retain the mismatch and require explicit workspace pins; this PR does not silently change search to match the old getter.
2. **Preserve legacy reader compatibility (implemented).** Inputs without `ownership: "explicit"` retain first-agent/default-marker inheritance. Broader normalization of those inputs is intentionally deferred, rather than changing the shipped reader contract in this fix.
3. **Keep adjacent policy out of scope (recommended).** No context-limit merging changes, shared-system-agent workspace redesign, public SDK entrypoint, or broader path allowlist. Those require separate decisions.
ClawSweeper's source review supports this direction but requests a compatibility decision; it is not maintainer approval. Please review the ownership choice before merge. Rollback is reverting this change; the issue's explicit-workspace-pin mitigation remains available.
## Evidence
### After-fix real Gateway proof — September 13, 2026
Built candidate `be5a589a0ee148b54c51f6876ef5281ee9b29cb5`, Node 26.8.1/macOS. Used a loopback-only Gateway, registered Memory Core tools through HTTP `POST /tools/invoke`, synthetic files and real on-disk SQLite state, `provider: none`, and vectors disabled. No test-helper imports, provider calls, or production configuration/state changes.
| Setup | Actual search → get result |
| --- | --- |
| Fresh explicit two-agent roster, neither agent pinned; parent contains a decoy | main: `USER.md`, `status=ok`, `Orchid proof main`; other: `USER.md`, `status=ok`, `Orchid proof other` |
| Fresh setup, parent traversal for each agent | `MEMORY_PATH_NOT_ALLOWED` |
| Installed 2026.9.3, existing explicit workspace pins | main/other: `USER.md`, `status=ok`, matching `Orchid upgrade main` / `Orchid upgrade other` |
| Same shipped-created state after candidate Doctor migration and candidate Gateway startup | Both agents return those same markers with `status=ok` |
For each agent, passed the search result's path/start line/inclusive line count into `memory_get`. Stopped the shipped Gateway before switching binaries. Candidate initially refused the older session-identity database; followed its supported `doctor --fix --non-interactive` instruction (exit 0, both agent databases v19→v20), then repeated the actual HTTP flow. Both existing workspace pins were preserved exactly, and SHA-256 checks showed all three synthetic `USER.md` files unchanged. Doctor did change normal config metadata/defaults; the whole config was **not** unchanged. All proof Gateways were stopped.
This is fresh-runtime and shipped-created-state/Doctor migration proof, **not** an installer, `openclaw update` driver, or live-user upgrade test. Retained legacy-owner shapes beyond these fixtures remain covered by focused tests, not claimed as live proof. The schema migration belongs to the candidate's existing upgrade path; this six-file patch introduces no schema or embedding-metadata changes. Maintainer acceptance of explicit-roster read-root alignment remains required.
CI at this evidence update: the model-login catalog timing case in [large-26](https://github.com/openclaw/openclaw/actions/runs/34761804381/job/103735936253) failed, making the aggregate gate red. Its fixture disables memory and uses one pinned agent without explicit ownership; likely unrelated, but no matching base failure was verified. No CI pass is claimed.
- Focused validation: 162 tests passed across six files (one existing skipped test), including the real-manager tool regression and package-boundary contracts.
- Final-head `node scripts/check-changed.mjs` passed: core and test types, core/extension lint, formatting, dead-export scans, package/SDK boundaries, and runtime import-cycle checks. `git diff --check` passed.
- Full `pnpm build` passed in 3m26s, including plugin SDK exports, CLI bootstrap imports, built plugin-loading checks, and Control UI build. After that build, the only patch change moved an unchanged re-export below imports for lint; the rebase added only unrelated ACP tests. The repository-wide test suite was not run.
- Tests-first reproduction on upstream main: both roster orders returned the parent decoy through `memory_get` after real SQLite-backed `memory_search` found the agent marker.
- The regression exercises both agents, both roster orders, returned path/line readback, and rejection of parent traversal. It uses temporary files and databases with embeddings disabled; no provider request or private installation data.
- Compatibility coverage includes explicit workspace overrides, retained/removed migration owners, ignored legacy markers under explicit ownership, optional legacy IDs, sole/duplicate agents, and absent/empty rosters. Existing legacy path, file-reader, core ownership, and package-boundary coverage is retained.
- Independent clean-context P0–P3 source review found no actionable findings. The repository Auto Review CLI was also attempted but could not authenticate; no successful CLI review is claimed.
AI-assisted implementation and review. Maintainer approval remains required; no merge or automatic repair authority is inferred from the issue labels.
---
## Maintainer addendum — September 14, 2026
The ownership choices proposed above are accepted. This addendum records the final shared legacy-data owner, the preserved durable runtime default, and the refreshed proof. Earlier evidence remains tied to its stated revision.
### Problem
Memory search finds an agent's file, but `memory_get` returns the parent's different file as success.
### Fix and impact
Search and explicit memory reads now share one lightweight legacy-data owner. The two existing cached facts stay with the agent owner. Raw reader behavior, optional IDs, workspace pins, home/profile handling, and extra paths remain supported. No schema, migration, config key, protocol, or dependency changes.
Owner decision: explicit-roster reads must return the searched workspace's file, never a different parent file. This ends the explicit-roster exception retained in #140984. To retain the parent as an agent's workspace, configure that workspace explicitly. Legacy data ownership remains separate from the durable runtime default, preserving #146246.
Production growth is reduced from 46 to 38 net lines:
- `agent-roster.ts` moves existing roster readers into a lightweight module and owns the shared data decision.
- `agent-scope-config.ts` shrinks by 96 lines while preserving public exports and both cache lifetimes.
- Memory-host `config-utils.ts` adds eight net lines to preserve raw-reader compatibility and use the shared owner for explicit inputs.
- `openclaw-runtime-paths.ts` adds the one export needed by that call.
### Evidence
- Main `5576f256da`: the three unpinned regression rows fail with `status: ok` and `text: Parent decoy`; the pinned control passes. Command: `node scripts/run-vitest.mjs extensions/memory-core/src/tools.real-manager.test.ts -t 'reads the indexed agent file with explicit ownership'`.
- Retained HTTP proof at accepted `a016f35b265a96d4cdfd5d43dbf12d461ae417d2` (the rebuilt branch has identical owner files): real Gateway `POST /tools/invoke`, search then get, covers both roster orders, a non-first system agent, explicit pins, and parent traversal. Eight reads return the known agent file bytes; eight traversals are rejected.
- Fresh merge `f48cf5fc4eb0e717f252fefde7929632f24d494a`, parents candidate above and main `f6d984fcbe`: 297 tests pass, one existing skip, across the owner, memory, session/auth, and Doctor suites below.
- Local production/test type checks, changed-file type-aware lint, formatting, architecture, unused-export, line-count, and assertion checks pass. The assertion allowance only shrinks.
- Retained 2026.9.3-created state proof preserves pins, file hashes, and runtime default through Doctor/restart. This proves state compatibility, not the installed update driver.
- Earlier local matrix replay was stopped; its historical failures remain recorded. Current exact-head CI run [34806299349](https://github.com/openclaw/openclaw/actions/runs/34806299349) completed successfully at `1c3602c201d65ed43e45c4c1c856fe4fcc696dc8`, with every executed job passing.
<details>
<summary>HTTP requests and fresh-merge test command</summary>
The isolated Gateway runs through `pnpm openclaw gateway run --port <isolated-port> --bind loopback`.
```json
{"agentId":"main","tool":"memory_search","args":{"query":"AGENT_MAIN_ORCHID","corpus":"memory"}}
{"agentId":"main","tool":"memory_get","args":{"path":"USER.md","from":1,"lines":3}}
{"agentId":"main","tool":"memory_get","args":{"path":"../USER.md","from":1,"lines":2}}
```
The search-returned path and line range drive the read. The read returns two lines: `# main`, followed by `AGENT_MAIN_ORCHID searched source`. Traversal returns `MEMORY_PATH_NOT_ALLOWED`.
```sh
node scripts/run-vitest.mjs \
packages/memory-host-sdk/src/host/config-utils.test.ts \
packages/memory-host-sdk/src/host/read-file.test.ts \
packages/memory-host-sdk/src/host/read-file-manager-compat.test.ts \
extensions/memory-core/src/tools.real-manager.test.ts \
src/agents/agent-scope-config.test.ts \
src/agents/legacy-inherited-auth-dir.test.ts \
src/plugins/contracts/extension-package-project-boundaries.test.ts \
src/config/legacy.roster.test.ts \
src/commands/sessions.default-agent-store.test.ts \
src/commands/doctor/shared/legacy-config-migrations.agent-rosters.test.ts \
src/commands/doctor/shared/legacy-config-migrations.runtime.system-agent.test.ts \
src/commands/doctor/shared/default-agent-role-materialization.test.ts \
src/commands/doctor/shared/default-agent-role-materialization.write.test.ts \
src/commands/doctor-config-flow.workspace-persistence.test.ts \
src/agents/agent-scope.test.ts \
src/agents/agent-scope.workspace-inference.test.ts
```
</details>
## Consumers
The moved roster types retain their `index`, `key`, and `kind` fields. The following typed read sites preserve their source-location contracts; generic word matches elsewhere are not consumers of these fields.
<details>
<summary>Preserved roster-field callers</summary>
census: generic index reviewed — 19 callers listed
- `src/agents/sandbox/secret-owner.ts:53:42` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-model-validation.ts:117:90`, `src/cli/config-model-validation.ts:221:62` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:90`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:90` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:85:35` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:90`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:90` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:993:71`, `src/config/io.write-prepare.ts:1000:41` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/validation-core.ts:159:36`, `src/config/validation-core.ts:281:90`, `src/config/validation-core.ts:335:55` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:90` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/secrets/runtime-config-collectors-core.ts:536:35` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:90` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:90` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:50:36` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:49:40` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
census: generic key reviewed — 18 callers listed
- `src/agents/sandbox/secret-owner.ts:52:45` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-model-validation.ts:117:60`, `src/cli/config-model-validation.ts:221:42` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:60`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:60` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:84:38` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:60`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:60` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:1001:44` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/validation-core.ts:158:39`, `src/config/validation-core.ts:281:60`, `src/config/validation-core.ts:334:58` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:60` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/secrets/runtime-config-collectors-core.ts:535:38` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:60` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:60` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:46:44` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:46:41` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
census: generic kind reviewed — 64 callers listed
- `src/agents/agent-roster.ts:23:15`, `src/agents/agent-roster.ts:35:15` — Select the raw representation before projecting entries; preserve entries precedence and reject invalid representation values.
- `src/agents/agent-scope-config.ts:281:15`, `src/agents/agent-scope-config.ts:294:15`, `src/agents/agent-scope-config.ts:329:15`, `src/agents/agent-scope-config.ts:334:15`, `src/agents/agent-scope-config.ts:316:47` — Preserve direct and mutable roster lookup branches; source.kind in the batch index preserves keyed clone-on-read and legacy list identity.
- `src/agents/sandbox/secret-owner.ts:51:25` — Report unresolved SSH secret references at the selected agent source path.
- `src/cli/config-cli-roster.ts:17:21`, `src/cli/config-cli-roster.ts:35:55`, `src/cli/config-cli-roster.ts:53:19`, `src/cli/config-cli-roster.ts:58:17`, `src/cli/config-cli-roster.ts:90:22`, `src/cli/config-cli-roster.ts:107:38` — Preserve list order and keyed/list path translation across CLI mutations and canonicalization.
- `src/cli/config-model-validation.ts:117:14`, `src/cli/config-model-validation.ts:220:14`, `src/cli/config-model-validation.ts:221:14` — Locate model validation issues and test whether edited paths change model ownership at the correct keyed entry or list index.
- `src/commands/doctor-config-flow.ts:255:45` — Read migrated keyed entries to persist the legacy workspace and stamp explicit ownership for multiple agents.
- `src/commands/doctor/shared/context-engine-host-compat.ts:113:14`, `src/commands/doctor/shared/context-engine-host-compat.ts:153:14` — Locate context-engine host compatibility warnings and repairs at the source agent path.
- `src/commands/doctor/shared/exec-safe-bins.ts:83:16` — Locate executable allowlist warnings at the source agent tools.exec path.
- `src/commands/doctor/shared/legacy-config-core-migrate.ts:30:41`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:35:25`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:45:19`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:46:20`, `src/commands/doctor/shared/legacy-config-core-migrate.ts:108:107` — Validate representation/value agreement, repair the selected roster in its existing shape, and name that shape in repair messages.
- `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:91:14`, `src/commands/doctor/shared/plugin-tool-allowlist-warnings.ts:293:14` — Locate plugin tool allowlist warnings at the source agent path.
- `src/config/io.write-prepare.ts:892:14`, `src/config/io.write-prepare.ts:951:20`, `src/config/io.write-prepare.ts:1146:15`, `src/config/io.write-prepare.ts:1177:25`, `src/config/io.write-prepare.ts:1185:26`, `src/config/io.write-prepare.ts:1218:23`, `src/config/io.write-prepare.ts:1233:23`, `src/config/io.write-prepare.ts:1255:25`, `src/config/io.write-prepare.ts:1269:23`, `src/config/io.write-prepare.ts:1287:21`, `src/config/io.write-prepare.ts:1330:23`, `src/config/io.write-prepare.ts:1352:21`, `src/config/io.write-prepare.ts:1354:21`, `src/config/io.write-prepare.ts:1450:23`, `src/config/io.write-prepare.ts:1455:27`, `src/config/io.write-prepare.ts:1467:42`, `src/config/io.write-prepare.ts:1532:72`, `src/config/io.write-prepare.ts:1546:23`, `src/config/io.write-prepare.ts:1551:25`, `src/config/io.write-prepare.ts:993:25`, `src/config/io.write-prepare.ts:999:22` — Preserve original roster representation, legacy occurrence identity, explicit writes/deletions, authored reference restoration, and source paths across config writes.
- `src/config/legacy.roster.ts:106:23`, `src/config/legacy.roster.ts:117:35` — Select legacy list conversion, then consume the keyed roster after migration.
- `src/config/validation-core.ts:157:12`, `src/config/validation-core.ts:281:14`, `src/config/validation-core.ts:333:34` — Locate avatar, model-policy, and sandbox environment errors at the authored keyed entry or original list index.
- `src/config/validation.ts:715:14` — Attach heartbeat target validation issues to the correct authored agent path.
- `src/gateway/server-methods/config.ts:528:15`, `src/gateway/server-methods/config.ts:531:15` — Enumerate explicitly authored agent IDs in either representation before rejecting unapproved removals.
- `src/plugins/provider-auth-choice-helpers.ts:304:52`, `src/plugins/provider-auth-choice-helpers.ts:306:52` — Write normalized agent model references back using the original keyed or list representation.
- `src/secrets/runtime-config-collectors-core.ts:534:16` — Locate agent text-to-speech secret assignments at the source agent path.
- `src/secrets/runtime-config-collectors-memory.ts:125:14` — Locate memory secret assignments at the source agent path; the separate unchanged implicit-agent producer supplies the legacy key when no roster exists.
- `src/secrets/runtime-config-collectors-sandbox.ts:75:14` — Carry the source agent path into sandbox secret collection candidates.
- `src/security/dangerous-config-flags-core.ts:45:21` — Report dangerous flags at canonical keyed paths when an ID exists; retain original list index for an ID-less legacy row.
- `src/skills/workshop/tool-policy-diagnostic.ts:45:19` — Report canonical keyed tool-policy paths when an ID exists; retain original list index for an ID-less legacy row.
</details>
Registered memory tools use the shared data owner for explicit workspace reads. Search manager lookup, forget/reset/backfill, session/auth data locations, and Doctor materialization retain their existing owner contracts. There is no new state writer; config-identity provenance and the separate data/runtime batch facts keep their existing lifetimes.
### Contributor-branch refresh
The accepted content is rebuilt on the original contributor head `be5a589a0ee148b54c51f6876ef5281ee9b29cb5`, followed by a plain merge of main `ae5a4b4a55` and the five maintainer commits in order. There were no conflicts or manual resolutions, and all five messages are unchanged.
The resulting head `1c3602c201d65ed43e45c4c1c856fe4fcc696dc8` has the same complete tree as the accepted revision merged with that main. The 16-file command above passed again on this head: 297 tests, one existing skip, exit 0.
The unchanged local preflight passed on this same head in a physical checkout with its own dependencies. It covered type-aware lint, production types, core/extension/script/root-test types, and protocol checks. The initial nested-checkout attempt failed only the declaration-input isolation guard; physical isolation resolved it without disabling any check.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* feat: start remote sessions without a repository
Offer a fresh isolated workspace for cloud and paired-device sessions while preserving explicit and saved repository choices. Use a private empty backing repository per session through the existing managed-worktree lifecycle, retaining reclaim, snapshot restore, and cleanup without copying the agent workspace.
* fix: preserve workspace intent and independent snapshot cleanup
Retain legacy source selections before Git discovery updates availability, keep ordinary snapshot expiry independent of allocation contention, and update the complete source-selection browser flow. Keep request mapping and validation at their existing owners while satisfying import-cycle and export checks.
* fix: retain allocation and cleanup error causes
* test: expect normalized workspace source preferences
Keep API, endpoint, and request override facts tied to the selected configured model. Reuse the existing configured-row resolver and remove duplicate prefix stripping and normalized-row merging from harness support.
Preserve literal legacy selection, legacy-only fallback, and same-spelling duplicate semantics. Registered-harness regressions fail in both conflicting row orders before the fix; 203 focused tests, the changed-file gate, and independent review pass.
Related: #130706, #143822.