Skip to content

fix: keep explicit runtime resumes strict across reconnects - #925

Merged
vincentkoc merged 2 commits into
openclaw:mainfrom
taras:codex/strict-explicit-resume
Oct 31, 2026
Merged

vincentkoc merged 2 commits into
openclaw:mainfrom
taras:codex/strict-explicit-resume

Conversation

@taras

@taras taras commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

An explicit resumeSessionId can reuse an unprompted local record that originated with session/new. If a later cold load fails with -32603, the runtime currently creates a replacement and sends the requested prompt or control. This change retains the explicit constraint in the existing runtime manager and enforces it through both reconnect paths before replacement creation. Reusing the existing projection preserves saved model and effort when later callers omit them.

Same-record aliases and normal close preserve strictness. First adding the constraint during active untargeted work returns the existing unfinished-work error; explicit lifecycle preparation remains separate. Another runtime instance must receive the target again. No public parameter or persisted field is added.

Validation on Node 26.5.1 with pinned pnpm 11.27.1:

  • pnpm run check passes: formatting, typecheck, lint, builds, full tests and coverage gates. The full corpus reports 3,083 passed, 19 skipped and zero failures; the separate coverage selection reports 220 passed.
  • pnpm run check:docs and pnpm exec markdownlint-cli2 CHANGELOG.md pass.
  • Seven explicit-resume regressions pass; unchanged upstream fails both cold-load refusal expectations. Separate-process witnesses import the built public runtime and use its file store for control/prompt refusal and successful continuation with omitted Luna/xhigh settings.
  • Shared isolated Codex autoreview completed with no actionable P0–P2 findings. A separate architecture assessment passed the bounded owner/lifetime behavior.

Prepared with AI assistance.

Executed reconnect evidence

Baseline production: 6ce38c34f4e5aef16bf28ca8b1fb1e6650695c60. Patched production: 12fcca9ffc1417838b20b26e35518f38c8292a0c. Node v26.5.1, pnpm 11.27.1, ACP SDK 1.9.0, macOS. The same checked-in SDK subprocess mock from the patched commit supplies the controlled -32603 failure in both builds. Baseline src/ is byte-checked against its Git commit; no production fix is present there.

These are actual separate-process executions of dist/runtime.js with createFileSessionStore, a saved origin-new record and omitted settings on reattachment. They reuse the original witness, with paths parameterized and request telemetry added. Each world owns its state/home. No model service is contacted. The mock deliberately returns the fixed synthetic locator authority-session: session/new transcripts, rather than locator equality, expose replacement. This demonstrates the runtime bug/fix; it is not a live model-history claim.

The seed issues two configuration requests (gpt-5.6-luna, xhigh) and no prompt. Counts below exclude those seed requests. The two control requests during successful continuation are the runtime replaying those saved selections. The refusal phases issue neither a replacement nor downstream prompt/control work. Baseline exit1 is the expected failure of the strict-refusal assertion, with the forbidden requests still printed in finally.

Operation Exit Replacement session/new Controls after seed Prompts Saved model / effort
baseline-control-refuse 1 1 1 0 second-model / low
baseline-prompt-refuse 1 1 2 1 gpt-5.6-luna / xhigh
patched-control-refuse 0 0 0 0 gpt-5.6-luna / xhigh
patched-control-continue 0 0 2 1 gpt-5.6-luna / xhigh
patched-prompt-refuse 0 0 0 0 gpt-5.6-luna / xhigh
Actual stdout and process exit codes
baseline-control-seed (exit 0)
{"phase":"seed","outcome":"passed"}
{"phase":"seed","observation":{"sessions":["new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

baseline-control-refuse (exit 1)
{"phase":"refuse-control","observation":{"sessions":["new","load","new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"},{"sequence":1,"method":"session/set_config_option","value":"second-model"}],"prompts":[],"replacementSessionCalls":1,"controlCallsAfterSeed":1,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"second-model","system_prompt":"original immutable instructions"},"desired":{"effort":"low"}}}

baseline-prompt-seed (exit 0)
{"phase":"seed","outcome":"passed"}
{"phase":"seed","observation":{"sessions":["new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

baseline-prompt-refuse (exit 1)
{"phase":"refuse-prompt","observation":{"sessions":["new","load","new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"},{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[{"sessionId":"authority-session","prompt":[{"text":"do not dispatch","type":"text"}]}],"replacementSessionCalls":1,"controlCallsAfterSeed":2,"promptCalls":1,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

patched-control-seed (exit 0)
{"phase":"seed","outcome":"passed"}
{"phase":"seed","observation":{"sessions":["new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

patched-control-refuse (exit 0)
{"phase":"refuse-control","outcome":"passed"}
{"phase":"refuse-control","observation":{"sessions":["new","load"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

patched-control-continue (exit 0)
{"phase":"continue","outcome":"passed"}
{"phase":"continue","observation":{"sessions":["new","load","load"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"},{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[{"sessionId":"authority-session","prompt":[{"text":"continue exact conversation","type":"text"}]}],"replacementSessionCalls":0,"controlCallsAfterSeed":2,"promptCalls":1,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

patched-prompt-seed (exit 0)
{"phase":"seed","outcome":"passed"}
{"phase":"seed","observation":{"sessions":["new"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

patched-prompt-refuse (exit 0)
{"phase":"refuse-prompt","outcome":"passed"}
{"phase":"refuse-prompt","observation":{"sessions":["new","load"],"controls":[{"sequence":1,"method":"session/set_config_option","value":"gpt-5.6-luna"},{"sequence":2,"method":"session/set_config_option","value":"xhigh"}],"prompts":[],"replacementSessionCalls":0,"controlCallsAfterSeed":0,"promptCalls":0,"backendSessionId":"authority-session","seedBackendSessionId":"authority-session","createdAtUnchanged":true,"eventLogAssociationUnchanged":true,"sessionOptions":{"model":"gpt-5.6-luna","system_prompt":"original immutable instructions"},"desired":{"effort":"xhigh"}}}

Baseline assertion diagnostics are Missing expected rejection for control and actual completed / expected failed for prompt. Original complete stderr/receipts remain retained privately; private stack paths are omitted here. The JSON above is unedited stdout and includes the saved projection plus request transcript. createdAtUnchanged and eventLogAssociationUnchanged compare against the saved seed, including after successful continuation.

Reproduce against both exact revisions

Use the pinned pnpm version. The fixture is identical in both trees; copying it adds the controlled load-failure/effort support, while baseline production stays unchanged. Place the witness below at the indicated path before its invocations. The original execution used isolated prepared dependencies; the normal fresh-checkout preparation is shown here.

git clone https://github.com/taras/acpx.git acpx-proof
cd acpx-proof
git fetch origin codex/strict-explicit-resume
git checkout --detach 12fcca9ffc1417838b20b26e35518f38c8292a0c
pnpm install --frozen-lockfile
pnpm run build
pnpm run build:test
patched_repo="$PWD"
proof_dir="$(mktemp -d)"
baseline_repo="$proof_dir/baseline"
git worktree add --detach "$baseline_repo" 6ce38c34f4e5aef16bf28ca8b1fb1e6650695c60
git show 12fcca9ffc1417838b20b26e35518f38c8292a0c:test/fixtures/control-authority-agent.ts > "$baseline_repo/test/fixtures/control-authority-agent.ts"
cd "$baseline_repo"
pnpm install --frozen-lockfile
pnpm run build
pnpm run build:test
cd "$patched_repo"
# Save the witness below as "$proof_dir/witness.mjs".
mkdir -p "$proof_dir/baseline-control/home" "$proof_dir/baseline-prompt/home" "$proof_dir/patched-control/home" "$proof_dir/patched-prompt/home"
HOME="$proof_dir/baseline-control/home" ACPX_PROOF_REPO="$baseline_repo" node "$proof_dir/witness.mjs" seed "$proof_dir/baseline-control"
HOME="$proof_dir/baseline-control/home" ACPX_PROOF_REPO="$baseline_repo" node "$proof_dir/witness.mjs" refuse-control "$proof_dir/baseline-control"
# Expected exit1: baseline creates a replacement and sends the control.
HOME="$proof_dir/baseline-prompt/home" ACPX_PROOF_REPO="$baseline_repo" node "$proof_dir/witness.mjs" seed "$proof_dir/baseline-prompt"
HOME="$proof_dir/baseline-prompt/home" ACPX_PROOF_REPO="$baseline_repo" node "$proof_dir/witness.mjs" refuse-prompt "$proof_dir/baseline-prompt"
# Expected exit1: baseline creates a replacement and sends the prompt.
HOME="$proof_dir/patched-control/home" ACPX_PROOF_REPO="$patched_repo" node "$proof_dir/witness.mjs" seed "$proof_dir/patched-control"
HOME="$proof_dir/patched-control/home" ACPX_PROOF_REPO="$patched_repo" node "$proof_dir/witness.mjs" refuse-control "$proof_dir/patched-control"
HOME="$proof_dir/patched-control/home" ACPX_PROOF_REPO="$patched_repo" node "$proof_dir/witness.mjs" continue "$proof_dir/patched-control"
HOME="$proof_dir/patched-prompt/home" ACPX_PROOF_REPO="$patched_repo" node "$proof_dir/witness.mjs" seed "$proof_dir/patched-prompt"
HOME="$proof_dir/patched-prompt/home" ACPX_PROOF_REPO="$patched_repo" node "$proof_dir/witness.mjs" refuse-prompt "$proof_dir/patched-prompt"
Complete model-free built-public-runtime witness
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
const repo = path.resolve(process.env.ACPX_PROOF_REPO ?? '.');
const { createAcpRuntime, createAgentRegistry, createFileSessionStore } = await import(pathToFileURL(path.join(repo, 'dist/runtime.js')));
const [phase, directory] = process.argv.slice(2);
await fs.mkdir(directory, { recursive: true });
const store = createFileSessionStore({stateDir:path.join(directory,'raw')});
const runtime = createAcpRuntime({
  cwd:directory, sessionStore:store, permissionMode:'deny-all',
  agentRegistry:createAgentRegistry({overrides:{mock:[process.execPath,path.join(repo, 'dist-test/test/fixtures/control-authority-agent.js'),directory,'config']}}),
});
const input={sessionKey:'public-explicit',agent:'mock',mode:'persistent'};
const read=name=>fs.readFile(path.join(directory,name),'utf8');
const save=(name,value)=>fs.writeFile(path.join(directory,name),JSON.stringify(value,null,2)+'\n');
try {
 if(phase==='seed') {
  await fs.writeFile(path.join(directory,'release-prompt'),'release');
  await fs.writeFile(path.join(directory,'received.jsonl'),'');
  const handle=await runtime.ensureSession({...input,sessionOptions:{systemPrompt:'original immutable instructions'}});
  await runtime.setConfigOption({handle,key:'llm',value:'gpt-5.6-luna'});
  await runtime.setConfigOption({handle,key:'effort',value:'xhigh'});
  await runtime.close({handle,reason:'cold explicit attachment'});
  const record=await store.load(handle.acpxRecordId);
  assert.equal(record.acpx.session_origin,'new'); assert.deepEqual(record.messages,[]);
  await save('seed.json',{handle,record});
 } else {
  const seed=JSON.parse(await read('seed.json'));
  const before=await store.load(seed.handle.acpxRecordId);
  const handle=await runtime.ensureSession({...input,resumeSessionId:seed.handle.backendSessionId});
  await runtime.ensureSession(input); // compatible untargeted alias does not relax
  if(phase==='refuse-control'||phase==='refuse-prompt') {
   await fs.writeFile(path.join(directory,'fail-load'),'enabled');
   const controls=await read('received.jsonl');
   if(phase==='refuse-control') {
    await assert.rejects(runtime.setConfigOption({handle:seed.handle,key:'llm',value:'second-model'}),/could not be resumed/);
   } else {
    const turn=runtime.startTurn({handle,text:'do not dispatch',requestId:phase,mode:'prompt'});
    assert.equal((await turn.result).status,'failed');
   }
   assert.equal(await read('sessions.jsonl'),'"new"\n"load"\n');
   assert.equal(await read('received.jsonl'),controls);
   await assert.rejects(read('prompts.jsonl'),{code:'ENOENT'});
  } else if(phase==='continue') {
   await fs.rm(path.join(directory,'fail-load'),{force:true});
   const turn=runtime.startTurn({handle,text:'continue exact conversation',requestId:phase,mode:'prompt'});
   assert.equal((await turn.result).status,'completed');
   assert.equal((await runtime.getStatus({handle})).models.currentModelId,'gpt-5.6-luna');
   assert.equal(await read('sessions.jsonl'),'"new"\n"load"\n"load"\n');
  } else { throw new Error('Unknown phase'); }
  const after=await store.load(handle.acpxRecordId);
  assert.equal(after.acpSessionId,seed.record.acpSessionId);
  assert.equal(after.createdAt,seed.record.createdAt);
  assert.deepEqual(JSON.parse(JSON.stringify(after.eventLog)),seed.record.eventLog);
  assert.deepEqual(after.acpx.session_options,seed.record.acpx.session_options);
  assert.equal(after.acpx.session_options.model,'gpt-5.6-luna');
  assert.equal(after.acpx.desired_config_options.effort,'xhigh');
  if(phase==='refuse-control') {assert.deepEqual(after.messages,before.messages);}
  await save(phase+'.observation.json',{phase,backendSessionId:after.acpSessionId,createdAt:after.createdAt,eventLog:after.eventLog,sessionOptions:after.acpx.session_options,desired:after.acpx.desired_config_options,sessions:await read('sessions.jsonl'),controls:await read('received.jsonl')});
 }
 console.log(JSON.stringify({phase,outcome:'passed'}));
} finally {
 await runtime.shutdown();
 const lines = async name => (await read(name).catch(() => '')).trim().split('\n').filter(Boolean).map(line => JSON.parse(line));
 const sessions = await lines('sessions.jsonl');
 const controls = await lines('received.jsonl');
 const prompts = await lines('prompts.jsonl');
 const record = await store.load('public-explicit');
 const seed = JSON.parse(await read('seed.json'));
 console.log(JSON.stringify({phase, observation:{
  sessions, controls, prompts,
  replacementSessionCalls:sessions.filter(value=>value==='new').length-1,
  controlCallsAfterSeed:controls.length-2, promptCalls:prompts.length,
  backendSessionId:record.acpSessionId, seedBackendSessionId:seed.record.acpSessionId,
  createdAtUnchanged:record.createdAt===seed.record.createdAt,
  eventLogAssociationUnchanged:JSON.stringify(record.eventLog)===JSON.stringify(seed.record.eventLog),
  sessionOptions:record.acpx.session_options, desired:record.acpx.desired_config_options
 }}));
}

Repository CI also passed on the reviewed pre-integration head 12fcca9ffc1417838b20b26e35518f38c8292a0c: https://github.com/openclaw/acpx/actions/runs/41171242678 (attempt2). The later human approval is retained: #925 (review). That evidence update left implementation and the then-draft state unchanged; the author has since marked the PR ready for review.

Maintainer integration validation

Integrated main ba09c499a4c5547981e65c087d8a0902af217fdd at head e3d35cc7a26e3582b1c45c0b46d97ab43d19312e. The only manual conflict was in reconnect.ts: retain the strict-resume decision while preserving the intentional removal of subsumed error classification from #950. The complete introduced patch across all seven files is identical to reviewed head 12fcca9ffc1417838b20b26e35518f38c8292a0c; no retired helper was restored. git diff --check and the outgoing personal-data scan pass.

Fresh exact-head integration CI passed all 12 jobs, including Node 22/24/26, coverage, mutation, docs, formatting, types, lint, builds, conformance, and Slophammer: https://github.com/openclaw/acpx/actions/runs/41211913884. This checkout is code-only; no contributor code or dependency-backed tests were executed locally for this integration. The existing independent review and public-runtime red/green evidence remain applicable.

@clawsweeper

clawsweeper Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 31, 2026
@clawsweeper

clawsweeper Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes

The runtime retains explicit session-resume constraints across record reuse and later prompt or control reconnects.

Example: Attach with resumeSessionId="authority-session", then encounter a cold-load error -32603.

  • Before: An unprompted origin-new record permits a replacement session and dispatches the requested work.
  • After: Reconnect refuses replacement and downstream work while retaining saved gpt-5.6-luna and xhigh settings.

Review scores

Measure Result What it means
Overall readiness 🦞 diamond lobster (5/6) Focused contract repair with production-path request traces, meaningful regressions, unchanged storage format, passing exact-head checks, and recorded maintainer approval.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): Published macOS Node 26 executions import the built public runtime, use its file store and real SDK subprocess transport, inject a cold-load failure, and show zero replacement or downstream requests for patched prompt/control refusal plus successful continuation with saved settings intact.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: An explicit conversation attachment can later send work to a replacement backend instead of failing to resume the requested session.
Reason: The change restores the documented continuity contract, and member vincentkoc explicitly approved this behavior on the reviewed head.

Regression provenance

Regression provenance — verified: source commit b3cd84337952 by Michel Tomas; canonical PR #858 (blame-to-merge-commit).

Merge readiness

✅ Ready for maintainer review

This PR fixes a current-main session-continuity bug with no actionable findings; the previous draft-state concern is resolved and the reviewed head has maintainer approval.

Priority: P2
Reviewed head: 12fcca9ffc1417838b20b26e35518f38c8292a0c

Before merge

None.

Findings

None.

Agent review details

How this fits together

The embedded runtime manager receives session attachments, prompts, and controls, coordinates stored records and live owners, and sends ACP requests through the shared reconnect path.

flowchart TD
  A[Embedding caller] --> B[Runtime manager]
  B --> C[Session record store]
  B --> D[Record resume constraint]
  D --> E[Shared reconnect path]
  E --> F[ACP adapter]
  E --> G[Resume failure]
Loading

Technical review

Best possible solution:

Land the focused manager-owned constraint through the shared reconnect path while preserving ordinary recovery and explicit lifecycle reset.

Do we have a high-confidence way to reproduce the issue?

The supplied baseline traces reproduce replacement after an explicit compatible attachment, and current-main source retains the same causal path.

Is this the best way to solve the issue?

The fix preserves the existing recovery contract using the current manager and reconnect owners, without adding public parameters or persisted fields.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 23b0906f7174.

Provenance checked

  • Origin-new startup recovery keeps the original intent (b3cd843: Recover empty newly created backends after -32603 while retaining strict continuity for explicit resumes and existing conversation history.)
  • Compatible record reuse and unfinished-work admission keeps the original intent (1dfb711: Preserve compatible identity reads and prevent replacement or ownership changes while turns or controls remain unfinished.)
  • Explicit fresh-session preparation keeps the original intent (53177fc: Settle previously submitted session work before recording an intentional fresh-session reset.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

What I checked:

  • Pinned patch: The introduced diff retains the constraint in the existing manager and forwards it through both prompt and control reconnect paths. (src/runtime/engine/manager.ts:865, 12fcca9ffc14)
  • Current main still permits the failure: Current main allows fresh recovery for an unprompted origin-new record after -32603 without retaining a later explicit attachment constraint. (src/runtime/engine/reconnect.ts:910, 23b0906f7174)
  • Original recovery contract: The merged recovery change explicitly intended native resumes to retain their original backend; this PR closes the compatible-record reuse gap while preserving untargeted startup recovery. fix(sessions): recover startup only for newly created sessions #858. (src/runtime/engine/reconnect.ts:936, b3cd84337952)
  • Executed production-path evidence: The full PR body supplies exact-revision built-runtime executions using file storage and a real SDK subprocess transport: baseline dispatches forbidden replacement work, patched prompt/control refusal dispatches none, and successful continuation preserves identity and settings. Body SHA-256 matches c4a3c0894ec652984b79fd59ea2202db141321b4ba05c8731b59d5fa31588380. (12fcca9ffc14)
  • Maintainer approval and resolved previous step: Member vincentkoc approved this exact head and its intended strict-resume behavior; the PR is now ready for review, satisfying the previous review's only remaining step. fix: keep explicit runtime resumes strict across reconnects #925 (review). (12fcca9ffc14)
  • Exact-head validation: GitHub reports successful build, lint, typecheck, mutation, documentation, conformance, and test checks on the reviewed head, with no failed checks. (12fcca9ffc14)

Likely related people:

  • Peter Steinberger: Raw commit 1dfb711 adds src/runtime/engine/manager.ts:985 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 1dfb7110126e; files: src/runtime/engine/manager.ts)
  • Michel Tomas: Raw commit b3cd843 adds src/runtime/engine/reconnect.ts:924 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: b3cd84337952; files: src/runtime/engine/reconnect.ts)
  • Vincent Koc: Raw commit 53177fc adds src/runtime/engine/manager.ts:1943 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 53177fc9f04c; files: src/runtime/engine/manager.ts)

Labels

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.

Label justifications:

  • P2: A bounded embedded-runtime reconnect case can silently substitute a backend session despite an explicit resume target.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.
  • proof: sufficient: Contributor real behavior proof is sufficient.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (2 earlier review cycles)
  • reviewed 2026-10-31T05:19:58.265Z sha 12fcca9 :: needs real behavior proof before merge. :: none
  • reviewed 2026-10-31T07:10:52.113Z sha 12fcca9 :: needs changes before merge. :: none

Reviewed October 31, 2026, 3:57 AM ET / 07:57 UTC (Revision 3).

@vincentkoc vincentkoc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head 12fcca9 with no actionable P0–P2 findings. The runtime keeps the explicit native-session constraint across compatible record aliases and both prompt/control reconnects, rejects adding it during unfinished untargeted work, and preserves saved model/effort on reuse. The focused regressions and public-runtime witnesses cover the original replacement-session failure. All repository CI checks now pass on this head: https://github.com/openclaw/acpx/actions/runs/41171242678.

Leaving the PR in draft as submitted. Please mark it ready when it is intended for landing.

taras commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

Published the requested reproducible behavior evidence in the PR body: #925 (comment)

It includes the exact baseline and patched revisions, complete model-free built-public-runtime witness, commands, actual per-process stdout, saved settings and request counts. Baseline cold control/prompt loads create a replacement; patched refusal sends zero replacements and zero downstream work; successful continuation keeps the saved identity and omitted model/effort. Production code and draft state remain unchanged, and the later human approval is preserved.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 31, 2026
@taras
taras marked this pull request as ready for review October 31, 2026 07:39
@taras
taras requested a review from a team as a code owner October 31, 2026 07:39

taras commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Oct 31, 2026
@vincentkoc vincentkoc self-assigned this Oct 31, 2026

@vincentkoc vincentkoc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved integrated head e3d35cc. All seven introduced file patches are identical to the previously reviewed and approved 12fcca9. The reconnect conflict preserves the strict-resume decision and main's intentional removal of the subsumed helper from #950. No retired path was restored.

The explicit target remains strict across compatible record aliases and prompt/control reconnects; saved model/effort and existing history are preserved. Existing independent review and public-runtime red/green witnesses remain applicable. The author has marked the PR ready, no substantive findings remain, and all 12 exact-head integration CI jobs passed: https://github.com/openclaw/acpx/actions/runs/41211913884. No contributor code was executed locally for this integration.

@vincentkoc
vincentkoc merged commit 37e343e into openclaw:main Oct 31, 2026
15 checks passed
@vincentkoc

Copy link
Copy Markdown
Member

Landed as 37e343e.

Explicit runtime resume targets now remain strict across matching record aliases and later prompt/control reconnects, preserving saved configuration and history. Integrated main while keeping all seven reviewed feature patches identical and preserving the removed-helper cleanup from PR 950. Reviewed head e3d35cc; all 12 integration CI jobs passed: https://github.com/openclaw/acpx/actions/runs/41211913884.

Thanks @taras!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants