Skip to content

fix(config): surface backup restore copy failures in audit and logs#70515

Merged
jalehman merged 4 commits intoopenclaw:mainfrom
davidangularme:fix/config-restore-false-success
May 1, 2026
Merged

fix(config): surface backup restore copy failures in audit and logs#70515
jalehman merged 4 commits intoopenclaw:mainfrom
davidangularme:fix/config-restore-false-success

Conversation

@davidangularme
Copy link
Copy Markdown
Contributor

Summary

  • Problem: During suspicious-read recovery in maybeRecoverSuspiciousConfigRead (async and sync), copyFile(backupPath, configPath) is wrapped in a bare catch {}. When the copy fails (disk full, EACCES), the code still logs "Config auto-restored from backup" and writes the audit record with valid: true.
  • Why it matters: Users and audit tooling believe the config was repaired when it was not. A corrupted config persists silently.
  • What changed: Capture the copyFile error, emit a distinct failure warning, set valid to restoredFromBackup, and persist restoreErrorCode / restoreErrorMessage on the audit record.
  • What did NOT change: No changes to the merge-patch pipeline, config reload watcher, or any other recovery path.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway / orchestration

Linked Issue/PR

  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: Bare catch {} around copyFile in io.observe-recovery.ts lines 659-662 swallowed all errors unconditionally.
  • Missing detection / guardrail: No test covered a failing copyFile during recovery.
  • Contributing context: Pattern appears in multiple sites in observe-recovery.ts, this PR addresses the backup-restore path.

Regression Test Plan (if applicable)

  • Coverage level:
    • Unit test
  • Target test or file: src/config/io.observe-recovery.test.ts
  • Scenario: records copyFile failure instead of falsely claiming restore succeeded — injects a failing copyFile (EACCES), asserts failure warning fires, success warning does not, audit record has restoredFromBackup: false, valid: false, restoreErrorCode: "EACCES".
  • Why smallest reliable guardrail: Unit test with injected fs failure covers the exact branch.

User-visible / Behavior Changes

  • Failed backup restores now log a warning instead of falsely claiming success.
  • Audit records for failed restores now show valid: false with error details.

Diagram (if applicable)

Before:
[copyFile fails] -> [log "restored from backup"] -> [audit: valid: true]

After:
[copyFile fails] -> [log "restore FAILED: <error>"] -> [audit: valid: false, restoreErrorCode: "EACCES"]

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (Ubuntu 24)
  • Runtime/container: Node 24

Steps

  1. Trigger a suspicious config read with a valid .bak file present
  2. Make the .bak -> config copyFile fail (simulate EACCES)
  3. Observe logs and audit record

Expected

  • Warning log: "Config auto-restore from backup failed"
  • Audit record: valid: false, restoreErrorCode: "EACCES"

Actual (before fix)

  • Success log: "Config auto-restored from backup"
  • Audit record: valid: true, no error info

Evidence

  • Failing test/log before + passing after

Human Verification (required)

  • Verified scenarios: Unit test with injected EACCES copyFile failure
  • Edge cases checked: Both async and sync recovery paths
  • What I did not verify: Full integration test with real disk-full scenario

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: Audit record schema gains two new nullable fields (restoreErrorCode, restoreErrorMessage)
    • Mitigation: Fields default to null on non-recovery paths, no breaking change for consumers

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR correctly fixes the silent-failure bug in both maybeRecoverSuspiciousConfigRead and maybeRecoverSuspiciousConfigReadSync where a bare catch {} swallowed copyFile errors and still emitted a "restored from backup" success log and valid: true audit record. The new extractRestoreErrorDetails helper, conditional log branching, and extended audit schema are all well-structured, and the non-recovery call sites in io.ts are correctly initialised to null. All remaining findings are P2.

Confidence Score: 5/5

Safe to merge; the bug fix is correct and the only open items are minor style/test-coverage suggestions.

All findings are P2 (missing sync test mirror, minor log message formatting). The core logic — capturing the error, branching the log, and persisting error fields in the audit record — is correct for both async and sync paths. Schema change is backward-compatible with null defaults on non-recovery paths.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/io.observe-recovery.test.ts
Line: 278-321

Comment:
**Sync path has no copyFile-failure test**

The new test covers `maybeRecoverSuspiciousConfigRead` (async), but `maybeRecoverSuspiciousConfigReadSync` — which received the identical fix — has no corresponding failure-injection test. If someone accidentally breaks the sync branch in the future, the suite would not catch it. A mirror of this test using `copyFileSync` would close the gap.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/config/io.observe-recovery.ts
Line: 688-696

Comment:
**Double colon in failure warning when error message exists**

When `restoreErrorDetails.message` is non-null the template produces:

```
Config auto-restore from backup failed: /path/config (h1, h2): EACCES: permission denied
```

The `)` is followed immediately by `: ` (from the template suffix), then the message itself starts with `EACCES:` — resulting in `…(h1, h2): EACCES: permission denied`. This is readable but slightly noisy. Moving the error detail inside the parentheses or using a different separator (e.g. ``) would make the message easier to parse in log aggregators.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(config): surface backup restore copy..." | Re-trigger Greptile

Comment on lines 278 to 321
});
});

it("records copyFile failure instead of falsely claiming restore succeeded", async () => {
await withSuiteHome(async (home) => {
const { deps, configPath, auditPath, warn } = makeDeps(home);
await seedConfigBackup(configPath, recoverableTelegramConfig);
const clobbered = await writeClobberedUpdateChannel(configPath);

const copyError = Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" });
const failingFs: ObserveRecoveryDeps["fs"] = {
...deps.fs,
promises: {
...deps.fs.promises,
copyFile: () => Promise.reject(copyError),
},
};
const recovered = await maybeRecoverSuspiciousConfigRead({
deps: { ...deps, fs: failingFs },
configPath,
raw: clobbered.raw,
parsed: clobbered.parsed,
});

expect((recovered.parsed as { gateway?: { mode?: string } }).gateway?.mode).toBe("local");
await expect(fsp.readFile(configPath, "utf-8")).resolves.toBe(clobbered.raw);
expect(warn).toHaveBeenCalledWith(
expect.stringContaining("Config auto-restore from backup failed:"),
);
expect(warn).not.toHaveBeenCalledWith(
expect.stringContaining("Config auto-restored from backup:"),
);

const observe = await readLastObserveEvent(auditPath);
expect(observe?.restoredFromBackup).toBe(false);
expect(observe?.valid).toBe(false);
expect(observe?.restoreErrorCode).toBe("EACCES");
expect(observe?.restoreErrorMessage).toBe("EACCES: permission denied");
});
});

it("dedupes repeated suspicious hashes", async () => {
await withSuiteHome(async (home) => {
const { deps, configPath, auditPath } = makeDeps(home);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Sync path has no copyFile-failure test

The new test covers maybeRecoverSuspiciousConfigRead (async), but maybeRecoverSuspiciousConfigReadSync — which received the identical fix — has no corresponding failure-injection test. If someone accidentally breaks the sync branch in the future, the suite would not catch it. A mirror of this test using copyFileSync would close the gap.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/io.observe-recovery.test.ts
Line: 278-321

Comment:
**Sync path has no copyFile-failure test**

The new test covers `maybeRecoverSuspiciousConfigRead` (async), but `maybeRecoverSuspiciousConfigReadSync` — which received the identical fix — has no corresponding failure-injection test. If someone accidentally breaks the sync branch in the future, the suite would not catch it. A mirror of this test using `copyFileSync` would close the gap.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 688 to +696

params.deps.logger.warn(
`Config auto-restored from backup: ${params.configPath} (${suspicious.join(", ")})`,
);
const restoreErrorDetails = restoredFromBackup
? { code: null, message: null }
: extractRestoreErrorDetails(restoreError);

if (restoredFromBackup) {
params.deps.logger.warn(
`Config auto-restored from backup: ${params.configPath} (${suspicious.join(", ")})`,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Double colon in failure warning when error message exists

When restoreErrorDetails.message is non-null the template produces:

Config auto-restore from backup failed: /path/config (h1, h2): EACCES: permission denied

The ) is followed immediately by : (from the template suffix), then the message itself starts with EACCES: — resulting in …(h1, h2): EACCES: permission denied. This is readable but slightly noisy. Moving the error detail inside the parentheses or using a different separator (e.g. ) would make the message easier to parse in log aggregators.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/io.observe-recovery.ts
Line: 688-696

Comment:
**Double colon in failure warning when error message exists**

When `restoreErrorDetails.message` is non-null the template produces:

```
Config auto-restore from backup failed: /path/config (h1, h2): EACCES: permission denied
```

The `)` is followed immediately by `: ` (from the template suffix), then the message itself starts with `EACCES:` — resulting in `…(h1, h2): EACCES: permission denied`. This is readable but slightly noisy. Moving the error detail inside the parentheses or using a different separator (e.g. ``) would make the message easier to parse in log aggregators.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

davidangularme added a commit to davidangularme/openclaw that referenced this pull request Apr 23, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@davidangularme
Copy link
Copy Markdown
Contributor Author

Addressed in 4da2add — added sync mirror test and moved error detail inside parens.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 29, 2026

Codex review: needs changes before merge.

What this changes:

The PR adds restore-copy error fields to config observe audit records, changes async and sync suspicious-read backup copy failures to log and audit failure instead of success, initializes null error fields on normal observe snapshots, and adds async/sync EACCES regression tests.

Required change before merge:

Safe narrow repair: add the missing changelog entry and rerun focused config validation/exact-head checks; no product, ownership, or security judgment is needed for that mechanical blocker.

Security review:

Security review cleared: Security review cleared: the diff is limited to config recovery logging/audit fields and tests, with no dependency, workflow, lockfile, package, secret-handling, network, or external code execution changes.

Review findings:

  • [P2] Add the required changelog entry — src/config/io.observe-recovery.ts:679-683
Review details

Best possible solution:

Land this focused restore-copy audit/logging fix after adding one active-version ### Fixes changelog bullet crediting @davidangularme and after exact-head checks are green; keep broader empty-catch cleanup in separate follow-up work.

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

Yes. A high-confidence reproduction is to trigger suspicious config read recovery with a valid .bak and inject EACCES from copyFile/copyFileSync; current main source shows the error is swallowed while success is logged and valid: true is audited.

Is this the best way to solve the issue?

No, not as-is. The code direction is the narrow maintainable fix for this bug, but the patch is not the best mergeable solution until it adds the required changelog entry and the exact PR head is green.

Full review comments:

  • [P2] Add the required changelog entry — src/config/io.observe-recovery.ts:679-683
    This changes user-visible recovery logs and config observe audit records, but the PR still does not touch CHANGELOG.md. Add a single active-version ### Fixes entry with contributor credit before merge.
    Confidence: 0.89

Overall correctness: patch is incorrect
Overall confidence: 0.9

Acceptance criteria:

  • pnpm test src/config/io.observe-recovery.test.ts
  • pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/config/io.audit.ts src/config/io.ts src/config/io.observe-recovery.ts src/config/io.observe-recovery.test.ts
  • pnpm lint:core
  • pnpm check:changed

What I checked:

  • Current main async false-success path: Current main catches backup copyFile errors with an empty catch, then logs Config auto-restored from backup and appends an observe audit record with valid: true. (src/config/io.observe-recovery.ts:641, 9300d48244ca)
  • Current main sync false-success path: The sync path has the same empty copyFileSync catch followed by the success warning and valid: true audit append. (src/config/io.observe-recovery.ts:731, 9300d48244ca)
  • Current audit schema lacks restore error fields: ConfigObserveAuditRecord currently ends at restoredBackupPath, so main has no restoreErrorCode or restoreErrorMessage field for failed restore diagnostics. (src/config/io.audit.ts:227, 9300d48244ca)
  • PR head implements focused behavior: PR head captures restore errors, conditionally logs failure, sets audit valid from restoredFromBackup, and writes restoreErrorCode/restoreErrorMessage for both async and sync paths. (src/config/io.observe-recovery.ts:679, d0d1cc204e98)
  • PR head adds async and sync regression tests: PR head injects EACCES from copyFile and copyFileSync and asserts failure logging plus audit valid:false, restoredFromBackup:false, and restore error fields. (src/config/io.observe-recovery.test.ts:281, d0d1cc204e98)
  • Required changelog entry is absent: The PR file list contains only src/config/io.audit.ts, src/config/io.observe-recovery.test.ts, src/config/io.observe-recovery.ts, and src/config/io.ts; CHANGELOG.md has no restoreError/backup-restore entry. (CHANGELOG.md:25, 9300d48244ca)

Likely related people:

  • steipete: GitHub path history for src/config/io.observe-recovery.ts shows repeated recent config observe/recovery work, including shared recovery helpers, invalid gateway recovery, critical clobber recovery, and the latest export trimming around this file. (role: introduced behavior and primary config recovery maintainer; confidence: high; commits: 83801c49f7a3, ffb1628727fd, 7b2c9a6fa3d3; files: src/config/io.observe-recovery.ts, src/config/io.audit.ts)
  • jalehman: Authored a recent config recovery rollback fix in the same recovery file and reopened/assigned this PR after the supersession discussion, making them a likely maintainer routing candidate. (role: recent adjacent maintainer and current PR router; confidence: medium; commits: f369939fedd5; files: src/config/io.observe-recovery.ts)
  • koshaji: Recently changed config audit redaction behavior touching the audit/recovery path, which is adjacent to the added observe audit fields. (role: recent config audit maintainer; confidence: medium; commits: a853c5e8c26f; files: src/config/io.audit.ts, src/config/io.observe-recovery.ts)

Remaining risk / open question:

  • The PR still lacks the required CHANGELOG.md entry for a user-facing fix.
  • Exact-head CI was not fully settled at review time: check, lint, type, and many shard checks were successful, but Scan changed paths (precise) reported failure and checks-node-core was still queued.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9300d48244ca.

@davidangularme
Copy link
Copy Markdown
Contributor Author

@clawsweeper flagged the overlap with #62665. I haven't read #62665's
diff in detail, so I can't say which is the cleaner implementation —
but the distinguishing additions in this PR are the
restoreErrorCode / restoreErrorMessage fields on the audit record
and the async + sync EACCES regression tests. Happy to defer to
maintainers' call on which becomes canonical. If #62665 wins, I can
close this one and reopen a follow-up that ports the audit-field +
regression-test additions onto whichever fix lands.

@jalehman jalehman self-assigned this Apr 29, 2026
davidangularme added a commit to davidangularme/openclaw that referenced this pull request Apr 30, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@davidangularme davidangularme force-pushed the fix/config-restore-false-success branch from 1de0f98 to 6376ce2 Compare April 30, 2026 03:10
@davidangularme
Copy link
Copy Markdown
Contributor Author

Hi @jalehman, I've addressed the Greptile feedback regarding the sync mirror tests and log formatting in the latest commit. The PR is ready on my end.

sallyom pushed a commit to sallyom/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.

(cherry picked from commit 6376ce2)
sallyom pushed a commit to sallyom/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.

(cherry picked from commit 6376ce2)
@sallyom
Copy link
Copy Markdown
Contributor

sallyom commented May 1, 2026

I have a PR to resolve #63423 that flagged this PR as overlapping! I pulled this PR’s restore-copy audit fixes into #75441 with @davidangularme 's commits cherry-picked and credited, so #75441 will resolve the full issue in one PR. @jalehman if you prefer to merge this separately first no worries, I'll adjust and rebase mine after.

@davidangularme
Copy link
Copy Markdown
Contributor Author

davidangularme commented May 1, 2026

@jalehman @steipete : Given the persistent CI failures and regressions in this PR, I am withdrawing my suggestion to land my recovery audit fix here. To maintain project stability, I now strongly recommend merging the focused, verified baseline from #75568. We shouldn't let a stable, verified fix be held back by unrelated integration issues and core regressions.

@clawsweeper clawsweeper Bot closed this May 1, 2026
@clawsweeper clawsweeper Bot mentioned this pull request May 1, 2026
25 tasks
@davidangularme
Copy link
Copy Markdown
Contributor Author

@jalehman @steipete , I'm seeing that the integrated alternative (#75441) is currently unstable and failing core CI checks. This PR (#70515) was already verified and green. If you want to unblock the main branch without the regressions introduced in the other PR, I recommend reopening and merging this focused fix. It's ready to go

@davidangularme
Copy link
Copy Markdown
Contributor Author

@jalehman A case study in how open-source contributions get lost — told entirely through public, verifiable facts.

The bug:
In openclaw, when a config backup restore fails (disk full, EACCES), the system logs "Config auto-restored from backup" and writes valid: true to the audit record. Users and audit tooling believe the config was repaired. It was not. A corrupted config persists silently.

What I did:
• Identified the root cause: a bare catch {} around copyFile in io.observe-recovery.ts (lines 659-662) that swallowed all errors unconditionally.
• Submitted PR #70515 with the fix, covering both async and sync recovery paths.
• Added two new audit fields: restoreErrorCode and restoreErrorMessage.
• Wrote regression tests with injected EACCES failures for both paths.
• Greptile's automated review: 5/5 confidence, "Safe to merge."
• Addressed every piece of review feedback (sync mirror test + log formatting) in a follow-up commit.
• CI was green. A maintainer (jalehman) self-assigned to the PR.

What happened next:
• Another contributor (sallyom) opened PR #75441, cherry-picking my commits, and labeling it "Includes and supersedes #70515."
• sallyom commented on my PR: "#75441 will resolve the full issue in one PR."
• An automated bot (clawsweeper) closed my PR as "duplicate/superseded."
• No human maintainer reviewed or approved this decision.

The factual problems with these claims:
• "Will resolve the full issue in one PR": The diff of #75441 only touches src/config/. Issue #63423 ("Empty catch blocks swallow errors silently") explicitly requires changes in src/pairing/ and src/infra/. These directories are untouched.
• "Fixes #63423" is used as a GitHub tag in the PR header. This is an auto-close tag — if merged, it would automatically close issue #63423 as resolved, even though the required files are not addressed. The project's bug tracking would be silently corrupted.
• "Supersedes #70515": The PR that claims to supersede mine has 5 failing CI checks and unresolved merge conflicts. The PR it replaced was green.
• The CI failures are caused by importing functions (observeConfigSnapshot, observeConfigSnapshotSync) that were recently moved to internal scope by a maintainer in commit ad3e4db. My original fix had no dependency on these functions.

What I did to try to resolve it:
• Offered to help debug the failing PR.
• Proposed landing the focused, verified fix first, then iterating on the broader scope separately.
• Created a standalone baseline branch (fix/config-restore-baseline, PR #75568) to give maintainers a clean path.
• Opened issue #75651 documenting the structural conflicts with evidence.

The response:
• PR #75568 was closed.
• Issue #75651 was closed as "not planned."
• The clawsweeper bot's own analysis on that issue confirmed every structural point I raised: "PR diff is config-only [...] with no src/pairing or src/infra changes" and "Current main keeps observe snapshot helpers internal."

The current state:
• The original bug is still on main.
• My verified, green fix sits in a closed PR.
• The PR that replaced it cannot merge.
• The bot that closed my work also produced the analysis that confirms the replacement doesn't do what it claims.

For context, this is not my first contribution to openclaw. PR #74453 (fix for voice-call webhook in-flight limiter) was merged.

I'm not posting this to call anyone out. Every commit, CI log, bot analysis, and comment is public. I'm posting it because this pattern — where a working contribution is absorbed into a larger, broken scope, the original is closed by automation, and the contributor loses all agency — is one of the invisible friction points that drives people away from open source.

The facts are the facts.

#OpenSource #SoftwareEngineering #CodeReview #DevCommunity

@jalehman jalehman reopened this May 1, 2026
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from 6376ce2 to ca50cee Compare May 1, 2026 16:25
jalehman pushed a commit to davidangularme/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@sallyom
Copy link
Copy Markdown
Contributor

sallyom commented May 1, 2026

@jalehman please disregard #75441 and continue only with this PR - mine will be a follow-up
@davidangularme I didn't realize it would be auto-closed, wasn't my intention, please carry on here

@jalehman
Copy link
Copy Markdown
Contributor

jalehman commented May 1, 2026

Sounds good @sallyom. Thanks for the followup and detailed analysis @davidangularme. Working on getting this through as your analysis is correct that this is not yet resolved.

Apologies for the delays. I'm a little slower on my reviews than usual due to a new baby in the family. There's a lot going on with this project for all of us, and sometimes we drop balls.

jalehman pushed a commit to davidangularme/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from d0d1cc2 to e71e97a Compare May 1, 2026 18:05
jalehman pushed a commit to davidangularme/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from e71e97a to d47d61f Compare May 1, 2026 18:06
jalehman pushed a commit to davidangularme/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from d47d61f to 5ff27cc Compare May 1, 2026 18:07
jalehman pushed a commit to davidangularme/openclaw that referenced this pull request May 1, 2026
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from 5ff27cc to e84155b Compare May 1, 2026 18:09
davidangularme and others added 4 commits May 1, 2026 11:10
Previously a failed copyFile during suspicious-read recovery was swallowed by
a bare catch, still logged as 'Config auto-restored from backup', and the
audit record was written with valid: true — making a silent failure look
like success. Capture the error, log a distinct failure warning with the
message, set valid to restoredFromBackup, and persist restoreErrorCode /
restoreErrorMessage on the observe audit record for both async and sync
paths.
Review follow-ups on openclaw#70515: add the parallel failure-injection test for
maybeRecoverSuspiciousConfigReadSync so the sync path has the same
coverage, and move the captured error message inside the suspicious-
reason parentheses in the failure warning so the line no longer reads
'failed: <path> (...): <msg>' (double colon) — it now reads
'failed: <path> (..., <msg>)' with a single trailing colon after 'failed'.
@jalehman jalehman force-pushed the fix/config-restore-false-success branch from e84155b to 7c77974 Compare May 1, 2026 18:10
@jalehman jalehman merged commit f8ffc3e into openclaw:main May 1, 2026
34 checks passed
@jalehman
Copy link
Copy Markdown
Contributor

jalehman commented May 1, 2026

Merged via squash.

Thanks @davidangularme!

ihnotic added a commit to ihnotic/openclaw that referenced this pull request May 1, 2026
* refactor: trim provider discovery internal exports

* refactor: trim voice runtime internal exports

* fix(whatsapp): stage qrcode runtime dependency

* refactor: trim stream helper internal exports

* refactor: trim provider constant exports

* fix: make Discord voice reconnect timing resilient

* refactor: trim onboarding internal helpers

* docs(sandboxing): clarify sandbox setup scripts require source checkout (openclaw#75594)

Add inline docker build commands for npm-installed users who don't have the
source checkout scripts. Update all docs referencing sandbox-setup.sh,
sandbox-common-setup.sh and sandbox-browser-setup.sh to note they are
source-checkout-only and link to the new inline instructions.

Fixes openclaw#75485.

* fix(plugins): skip update when bundled plugin version is newer than installed clawhub/marketplace version (openclaw#75604)

* fix(plugin-sdk): restore deprecated reply pipeline compat exports

* refactor: trim test support helper exports

* test: stabilize Parallels update smokes

* refactor: trim sessions spawn harness type exports

* fix(gateway): refresh stale channel health cache

* fix(agent): apply configured fast mode to embedded runs

* fix(agent): default missing model cost metadata

* fix(agent): honor explicit OpenAI SSE transport

* fix(extensions): guard model and Twilio fetches

* fix(cli): repair agent runtime deps during startup

* fix(whatsapp): mirror qrcode from root runtime deps

* refactor: trim sanitize history harness exports

* refactor: trim subagent test helper exports

* [codex] Defer status reaction cleanup (openclaw#75582)

Summary:
- The PR updates the shared status reaction controller to track active remove-capable reactions, defer cleanup until clear/restoreInitial, adjust controller and Slack lifecycle tests, add a changelog entry, and carries qrcode runtime-dependency mirror hunks from its older base.

ClawSweeper fixups:
- Included follow-up commit: fix: limit status reaction restore cleanup
- Included follow-up commit: chore: merge main into status reaction cleanup
- Included follow-up commit: fix: mirror qrcode runtime dependency

Validation:
- ClawSweeper review passed for head f3efcb4.
- Required merge gates passed before the squash merge.

Prepared head SHA: f3efcb4
Review: openclaw#75582 (comment)

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>

* refactor: trim runtime test helper type exports

* refactor: delete unused test helpers

* refactor: delete unused test support modules

* fix(agents): trim trailing assistant turns and rewrite blank user messages in session repair (openclaw#75606)

* fix(agents): trim trailing assistant turns and rewrite blank user messages in session repair

Session-file repair now:
- Trims trailing assistant messages so the JSONL never ends on
  role=assistant, preventing the Anthropic 400 prefill-loop that
  fires when thinking is enabled. (openclaw#75271)
- Rewrites blank-only user messages to a synthetic '(continue)'
  placeholder instead of dropping them, so strict providers
  (Qwen/mlx-vlm, Anthropic) no longer reject transcripts missing
  a user turn. (openclaw#75313)

Closes openclaw#75271, closes openclaw#75313.

* refactor: clean up comments in session-file repair

* fix(agents): preserve trailing assistant tool-call turns during session trim

Mirror the outbound guard (stripTrailingAssistantPrefillTurns):
skip assistant entries containing toolCall/toolUse/functionCall
blocks so transcript repair can synthesize missing tool results.

Addresses PR review feedback from clawsweeper on openclaw#75606.

* refactor: delete unused contract test helpers

* test: use built-in OpenAI provider in Windows smoke

* fix: recover Discord voice auto-join after resume

* refactor: trim cli test helper exports

* fix: send twilio notify twiml directly

* Prefer Codex native workspace tools (openclaw#75308)

Summary:
- The PR adds Codex dynamic-tool profile config defaulting to `native-first`, filters duplicate workspace/process/planning tools from Codex app-server thread payloads, keeps managed `web_search`, updates docs/manifest/config baselines/changelog, and adds regression tests.

ClawSweeper fixups:
- Included follow-up commit: test(codex): pin native-first tool catalog
- Included follow-up commit: chore(config): refresh generated schema baseline
- Included follow-up commit: chore: add codex native-first changelog
- Included follow-up commit: chore: move native-first changelog entry
- Included follow-up commit: chore: refresh config baseline after rebase

Validation:
- ClawSweeper review passed for head 30e5cec.
- Required merge gates passed before the squash merge.

Prepared head SHA: 30e5cec
Review: openclaw#75308 (comment)

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: pashpashpash <[email protected]>

* refactor: trim cli program test exports

* refactor: trim command test helper exports

* ci: stop parity gate on pull requests

* chore: update dependencies

* chore: sync whatsapp dependency lockfile

* fix(agents): dedupe messaging tool replies by route

* fix: handle EPIPE errors on child process stdin writes (openclaw#75602)

Fix three child-process stdin write paths that let async EPIPE errors
escape to uncaughtException and crash the gateway.

extensions/imessage/src/client.ts (the actual openclaw#75438 crash path):
- Add child.stdin.on('error') listener in start() to catch async EPIPE
  and reject all pending requests via failAll().
- Add write callback to request() stdin.write() that rejects the
  specific pending request on error, instead of leaving it hanging
  until timeout.

src/agents/mcp-stdio-transport.ts:
- Fix write callback race in send(): previously resolved the promise
  immediately when write() returned true, then the write callback with
  EPIPE would fire after the promise was already fulfilled. Now always
  settles the promise from the write callback so the outcome is known
  before resolving.

src/process/exec.ts:
- Add stdin.on('error') before writing input so EPIPE from a
  prematurely-exited child is swallowed — the process exit handler
  reports the real status.

One reporter observed a gateway crash after 10.5 hours of stable
uptime — a single EPIPE on an iMessage RPC child process stdin write
killed the gateway with code 1.

Fixes: openclaw#75438

* refactor: trim cron test helper exports

* refactor: delete unused repo scan helper

* fix: default Discord voice to explicit opt-in

* refactor: trim test utility exports

* fix: show google meet twilio call diagnostics

* build: refresh a2ui bundle hash

* refactor: trim auto reply test helper exports

* fix(app): retry device tokens on pinned gateways (openclaw#75537)

* test(e2e): add bundled plugin runtime smoke

* test(e2e): activate channel rows for runtime smoke

* test(e2e): let channel runtime smoke load channels

* test(e2e): tolerate missing pgrep in runtime smoke

* test(e2e): assert canonical TTS provider in smoke

* test(e2e): satisfy runtime smoke lint

* test(e2e): account for lazy plugin commands in smoke

* test(e2e): give plugin runtime RPCs more headroom

* test(e2e): share runtime deps across matrix probes

* test(e2e): skip lazy tool catalog probes

* test(e2e): isolate plugin matrix runtime deps

* test(e2e): configure tts provider sections in matrix

* fix(plugins): preserve requested speech fallback

* test(e2e): harden bundled plugin runtime smoke

* docs(changelog): credit plugin runtime smoke fix

* fix(plugins): scope requested speech providers

* fix(test): satisfy plugin smoke lint

* fix(test): tolerate channel readiness degradation

* refactor: trim gateway test helper exports

* fix: allow doctor repair size drops

* fix: async transcript I/O to unblock gateway event loop (openclaw#75595)

* fix: async transcript I/O to unblock gateway event loop

Two related fixes for event-loop starvation caused by synchronous file
operations on session transcript files during gateway hot paths.

## sessions.list: yield between transcript reads (openclaw#75330)

Extract filterAndSortSessionEntries() from listSessionsFromStore() and
add a new listSessionsFromStoreAsync() that yields to the event loop
via setImmediate every 10 session rows. The sessions.list RPC handler
now uses the async version.

The synchronous version is kept for callers that need it (sessions-
resolve visibility checks, embedded backends, subagent tools).

The dominant blocker is readSessionTitleFieldsFromTranscript(), which
performs fs.statSync + fs.openSync + fs.readSync (head) + fs.readSync
(tail) for every session row that requests derived titles or last-
message previews. With 100+ sessions, this blocks the event loop for
32-64 seconds, starving WebSocket heartbeats, channel I/O, and
concurrent RPC.

## session compaction: async file copy (openclaw#75414)

Add captureCompactionCheckpointSnapshotAsync() using fs.promises for
stat, copyFile, and unlink instead of fsSync equivalents. Switch both
compact.ts and compact.queued.ts to the async version.

The synchronous copyFileSync of large transcript files (20MB+ observed
in production) was blocking the event loop for the entire copy duration
— one reporter measured a 43-minute event loop block from a single
compaction checkpoint capture.

Refs: openclaw#75330, openclaw#75414

* test: cover async transcript I/O responsiveness

* fix: avoid sync checkpoint metadata reads

* refactor: trim agent test helper exports

* fix(gateway): preflight strict agent delivery

* fix(onboard): run noninteractive migration imports

* fix(discord): send component-only native replies

* fix(slack): send block-only slash replies

* fix(telegram): send interactive-only button replies

* fix(line): send quick-reply-only payloads

* fix(auto-reply): keep docking in direct chats

* fix(discord): pin text dm main route owner

* fix(channels): pin dm main route owners

* fix(update): verify daemon restart port

* fix(update): use service env for doctor

* refactor: trim status scan test exports

* slack: persist thread participation best-effort (openclaw#75583)

* refactor: delete unused test helper code

* fix(gateway): defer session store read maintenance

* discord: persist component registries best-effort (openclaw#75584)

* test: retry Windows Parallels agent turn

* fix: type non-interactive onboard prompter

* refactor: trim gateway test helper barrel

* fix: declare deepinfra manifest model discovery

* refactor: derive deepinfra catalog from manifest

* docs: note deepinfra model catalog migration

* refactor: trim gateway helper state exports

* fix: allow onboarding config size drops

* fix: declare groq setup auth metadata

* fix: declare groq manifest model catalog

* docs: note groq manifest catalog migration

* fix: carry matrix dm allowlist state

* refactor: trim shared test helper exports

* fix: preserve OpenAI Codex xhigh thinking policy

* fix(infra): block ambient Homebrew env vars from brew resolution (openclaw#74463)

* fix: address issue

* fix: address issue

* fix: address PR review feedback

* fix: address PR review feedback

* fix: address codex review feedback

* docs: add changelog entry for PR merge

* fix: declare venice manifest catalog metadata

* refactor: derive venice fallback catalog from manifest

* docs: note venice manifest catalog migration

* refactor: trim extension internal type exports

* fix(infra): block Windows system path env vars from workspace .env injection (openclaw#74456)

* fix: address issue

* fix: address PR review feedback

* fix: address codex review feedback

* fix: address codex review feedback

* fix: address codex review feedback

* docs: add changelog entry for PR merge

* Update CHANGELOG.md

* fix: block workspace CLOUDSDK_PYTHON override and always set trusted interpreter for gcloud (openclaw#74492)

* fix: address issue

* docs: add changelog entry for PR merge

* refactor: trim extension runtime barrels

* fix(plugins): scope install slot selection

* fix(plugins): satisfy slot registry type

* fix: declare zai manifest model catalog

* docs: note zai manifest catalog migration

* refactor: trim private extension exports

* test(docker): install procps for plugin watchdogs

* refactor: delete unused extension shared shims

* fix: declare stepfun setup auth metadata

* test(pairing): clear allowlist cache before read spy (openclaw#74147)

* fix: declare qianfan setup auth metadata

* fix(doctor): keep plugin runtime deps repair explicit (openclaw#75603)

* fix(doctor): keep plugin runtime deps repair explicit

* fix(doctor): keep plugin runtime deps repair explicit

* fix(doctor): keep plugin runtime deps repair explicit

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>

* feat(slack): publish App Home tab views

* fix(onboarding): scope post-config runtime deps (openclaw#75653)

* fix(whatsapp): drop stale qrcode runtime dependency

* fix(zai): satisfy catalog lint

* refactor: trim internal extension seams

* test: require parallels agent responses

* refactor: trim extension runtime reexports

* fix(feishu): recover WebSocket after SDK retry exhaustion (openclaw#73739)

* fix(feishu): recover WebSocket after SDK retry exhaustion

* fix(feishu): recover WebSocket after SDK retry exhaustion

---------

Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>

* feat(google-meet): add transcribe caption health

* refactor: trim extension test hooks

* test: configure parallels smoke provider timeout

* fix(doctor): keep noninteractive service repair explicit

* fix(plugins): use built code for tool discovery

* test(pairing): pass read spy path after cache reset

* refactor: trim extension helper shims

* fix(slack): declare Slack type dependency

* test(plugins): mock install slot registry

* docs(changelog): backfill 84e9463 qianfan and a4fd45c stepfun setup auth metadata

* test: quote parallels provider config json

* refactor: trim messaging runtime barrels

* test(plugin-sdk): align facade loader windows fast path

* refactor: trim extension barrel leftovers

* test(ci): stabilize pricing and codex web config checks

* refactor: trim channel dead exports

* fix(config): accept optional Codex search location

* test(config): isolate codex web schema acceptance

* refactor: trim extension shim reexports

* test(config): type fresh codex schema import

* refactor: trim browser action barrel

* docs(changelog): finalize 2026.4.30 notes

* refactor: trim provider model constants

* fix(channels): honor module loader native opt-out

* refactor: trim core command dead exports

* refactor: trim provider internal exports

* refactor: trim extension helper exports

* test(parallels): write Windows provider config via batch file

* refactor: trim browser route exports

* refactor: trim qqbot helper exports

* refactor: trim qqbot bridge exports

* test(parallels): expose portable Git to Windows agent turns

* refactor: trim qqbot utility exports

* fix(context-engine): honor assembled prompt authority in precheck (openclaw#74255)

Merged via squash.

Prepared head SHA: 650b023
Co-authored-by: 100yenadmin <[email protected]>
Co-authored-by: jalehman <[email protected]>
Reviewed-by: @jalehman

* refactor: trim qqbot internal types

* refactor: trim mattermost helper exports

* refactor: trim matrix helper exports

* refactor: trim tlon helper exports

* test(parallels): retry Windows agent idle exits

* refactor: trim browser helper types

* test(parallels): budget Windows agent retry

* fix(ci): keep tool display serialization local

* refactor: trim voice-call helper exports

* refactor: trim bluebubbles helper exports

* refactor: trim bluebubbles config helper exports

* fix(webchat): create dashboard sessions from New Chat (openclaw#73725)

Summary:
- The PR rewires Control UI/WebChat New Chat to create and switch to a dashboard session through `sessions.create`, adds guarded UI/session helper logic and regression tests, and updates the changelog.

ClawSweeper fixups:
- Included follow-up commit: fix(webchat): create dashboard sessions from New Chat

Validation:
- ClawSweeper review passed for head 983c634.
- Required merge gates passed before the squash merge.

Prepared head SHA: 983c634
Review: openclaw#73725 (comment)

Co-authored-by: vincentkoc <[email protected]>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>

* refactor: trim brave and diffs helper exports

* fix(discord): avoid resolving token during action discovery (openclaw#75424)

Summary:
- The PR changes Discord message-action discovery to inspect configured accounts without resolving bot tokens, resolves scoped channel SecretRefs during message-tool execution even with an injected config snapshot, adds regression tests and a changelog entry, and restores a tool-display serializer export.

ClawSweeper fixups:
- Included follow-up commit: fix(discord): avoid resolving token during action discovery
- Included follow-up commit: fix(tools): restore tool display serializer export

Validation:
- ClawSweeper review passed for head a2cd832.
- Required merge gates passed before the squash merge.

Prepared head SHA: a2cd832
Review: openclaw#75424 (comment)

Co-authored-by: Clawdbot <[email protected]>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>

* Mattermost: refresh slash callback command validation (openclaw#72923)

* fix(mattermost): refresh slash callback tokens

* fix(mattermost): reconcile slash callback method

* fix(mattermost): bound slash command lookups

* fix(mattermost): cache slash validation lookups

* fix(mattermost): refresh slash routing

* fix(mattermost): require slash callback secret

* fix(mattermost): rate limit slash validation

* fix(mattermost): throttle slash validation

* fix(mattermost): bound slash token cache

* fix(mattermost): sanitize slash callback logs

* fix(mattermost): avoid stale slash token cache

* fix(mattermost): scope slash token gate to command

* fix(mattermost): rate-limit slash validation

* fix(mattermost): redact slash validation errors

* fix(mattermost): satisfy slash sanitizer lint

* Move Mattermost slash refresh changelog entry to Unreleased Fixes

* Apply oxfmt accordion blank-line on Mattermost slash docs

---------

Co-authored-by: Devin Robison <[email protected]>

* refactor: trim discord helper exports

* refactor: trim discord internal helper exports

* refactor: trim discord monitor helper exports

* refactor: trim discord test helper exports

* refactor: trim feishu helper exports

* docs(doctor): clarify service repair prompts

Clarify when doctor reports service repair state versus when gateway install performs launcher writes.\n\nThanks @vincentkoc

* fix(security): keep plain audit off plugin runtimes

Keep routine security audit on config/filesystem checks by default, reserving plugin runtime collectors for deep audit paths.\n\nThanks @vincentkoc

* test(e2e): bound telegram rtt warm samples

* test(rtt): expose warm sample metrics

* test(e2e): target successful rtt samples

* test(e2e): measure telegram normal reply rtt

* test(e2e): allow rtt retries to reach sample target

* refactor: trim provider helper exports

* fix(ci): satisfy rtt lint rules

* refactor: trim google meet helper exports

* refactor: trim google meet transport exports

* refactor: trim google chat helper exports

* test(rtt): support main package measurements

* refactor: trim irc helper exports

* refactor: trim signal helper exports

* refactor: trim line helper exports

* test(plugins): materialize runtime deps fixtures

* test(parallels): force Windows OpenAI SSE smoke

* refactor: trim mattermost helper exports

* refactor: trim nextcloud talk helper exports

* refactor: trim provider helper exports

* fix(gateway): bound session transcript hot paths

Bound recent transcript reads and oversized injected-message writes across gateway session paths.\n\nThanks @vincentkoc

* fix(plugins): reuse cold inspect registry snapshots (openclaw#75620)

Summary:
- The PR reuses a request-scoped cold manifest registry/runtime context across plugin status and inspect report paths, threads that context through provider/setup/metadata helpers, adds targeted coverage, and adds a changelog entry.

ClawSweeper fixups:
- Included follow-up commit: fix(plugins): preserve setup auto-enable lookup

Validation:
- ClawSweeper review passed for head 4d8e8e2.
- Required merge gates passed before the squash merge.

Prepared head SHA: 4d8e8e2
Review: openclaw#75620 (comment)

Co-authored-by: Vincent Koc <[email protected]>

* fix(plugins): avoid source rebuilds for policy toggles

Reuse current installed-plugin registry records for policy-only enable and disable refreshes.\n\nThanks @vincentkoc

* refactor: trim msteams helper exports

* test(parallels): force POSIX OpenAI SSE smoke

* refactor: trim telegram helper exports

* refactor: trim whatsapp helper exports

* test(parallels): batch POSIX provider config

* refactor: trim slack helper exports

* refactor: trim matrix helper exports

* test(release): repair release validation checks

* refactor: trim voice call helper exports

* refactor: trim memory wiki helper exports

* refactor: trim nostr helper exports

* test(release): forward validation fixes

* test(release): run doctor fix in setup-entry e2e

* refactor: trim qa matrix helper exports

* refactor: trim memory core helper exports

* test(release): fix setup fallback loader validation

* refactor: trim file transfer helper exports

* refactor: trim tlon helper exports

* refactor: trim browser helper exports

* test(release): harden channel add setup fallback

* refactor: trim imessage helper exports

* test(ci): update imessage runtime api guard

* test(release): include mirrored root runtime deps

* refactor: trim qa lab helper exports

* fix(bluebubbles): UTI-aware audio attachment detection (openclaw#75488)

Co-authored-by: Omar Shahine <[email protected]>

* test(release): remove stale runtime deps local

* refactor: trim qqbot helper exports

* refactor: trim codex internal exports

* refactor: trim whatsapp test helper exports

* refactor: trim telegram test harness exports

* refactor: remove stale openrouter runtime barrel

* refactor: trim zalo helper exports

* test(release): harden docker release validation

* fix(rtt): parse telegram scenario list

* refactor: trim feishu lifecycle helper exports

* test(plugins): use valid plugin origin in loader test

* fix(rtt): wait between telegram samples

* fix(config): surface backup restore copy failures in audit and logs (openclaw#70515)

Merged via squash.

Prepared head SHA: 7c77974
Co-authored-by: davidangularme <[email protected]>
Co-authored-by: jalehman <[email protected]>
Reviewed-by: @jalehman

* refactor: trim zalouser helper exports

* fix: stop stale OpenAI Responses replay in Telegram

Drop incomplete OpenAI/Codex tool-call replay turns and avoid previous_response_id deltas when Responses store is disabled. Verified on Zeus with focused tests, build, UI build, channel probe, and live Telegram-session canary.

* fix: stop stale Telegram final-answer replay

Suppress stale replayed final answers after newer Telegram/tool turns, supersede overlapping runs, and preserve current anchored output when replay frames contain old finals. Verified locally and on live Zeus with Telegram multi-skill expected-answer checks and ZeusChat model-switch browser checks.

* Stop direct NO_REPLY fallback chatter (#11)

Co-authored-by: Zeus3000 <[email protected]>

* Repair mismatched reset transcript files (#13)

Co-authored-by: Zeus3000 <[email protected]>

* Promote substantive stale-replay commentary (#15)

Co-authored-by: Zeus3000 <[email protected]>

* fix: preserve Telegram transcript user turns

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Alex Knight <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: pashpashpash <[email protected]>
Co-authored-by: Shakker <[email protected]>
Co-authored-by: Pavan Kumar Gondhi <[email protected]>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Co-authored-by: Andrew <[email protected]>
Co-authored-by: 100yenadmin <[email protected]>
Co-authored-by: jalehman <[email protected]>
Co-authored-by: vincentkoc <[email protected]>
Co-authored-by: Conan-Scott <[email protected]>
Co-authored-by: Clawdbot <[email protected]>
Co-authored-by: Agustin Rivera <[email protected]>
Co-authored-by: Devin Robison <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Omar Shahine <[email protected]>
Co-authored-by: Omar Shahine <[email protected]>
Co-authored-by: Fred David blum <[email protected]>
Co-authored-by: davidangularme <[email protected]>
Co-authored-by: Zeus3000 <[email protected]>
lxe pushed a commit to lxe/openclaw that referenced this pull request May 6, 2026
…penclaw#70515)

Merged via squash.

Prepared head SHA: 7c77974
Co-authored-by: davidangularme <[email protected]>
Co-authored-by: jalehman <[email protected]>
Reviewed-by: @jalehman
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants