Skip to content

fix: migrate QQBot credential backups to SQLite KV#89597

Merged
steipete merged 3 commits into
mainfrom
fix/qqbot-cache-migration-cleanup
Jul 1, 2026
Merged

fix: migrate QQBot credential backups to SQLite KV#89597
steipete merged 3 commits into
mainfrom
fix/qqbot-cache-migration-cleanup

Conversation

@steipete

@steipete steipete commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

QQBot still carried four legacy file-backed runtime paths after its SQLite conversion:

  • credential backup JSON files
  • session JSON files
  • known-user JSON files
  • ref-index JSONL files

The caches are disposable and should not have compatibility readers. Credential backups are persistent recovery data, so deleting their JSON runtime path without a doctor migration would strand existing backups.

This is a follow-up to #84314 and the isolation issue #84313.

Why This Change Was Made

  • Make SQLite plugin state the only QQBot runtime store.
  • Delete the obsolete data-path helper and all JSON/JSONL fallback branches.
  • Export credential-backup import through the external QQBot doctor-contract-api instead of coupling core or setup code to QQBot.
  • Scan only the active OPENCLAW_STATE_DIR legacy path; never search other profiles or the ambient home directory.
  • Preserve recovery data on capacity, parse, filesystem, or database failures.
  • Harden successful migration: validate account filenames, ignore symlinks, prefer account-scoped backups over the singleton, archive migrated sources, and set archive permissions to 0600.
  • Make rollback concurrency-safe by removing only unchanged rows inserted by the current migration and restoring only missing snapshot rows.

The session, known-user, and ref-index files are caches and intentionally receive no migration. They rebuild in SQLite.

User Impact

After upgrading and running openclaw doctor --fix, existing active-profile QQBot credential backups move into the qqbot / credential-backups SQLite namespace. Migrated JSON sources remain as .migrated archives with mode 0600.

QQBot runtime no longer reads or writes legacy cache files. Failed or capacity-constrained credential imports leave their source files in place for a safe retry.

Migration trigger boundary is intentional: upgrade installs, explicit doctor runs, Gateway startup, and state-mutating CLI commands run state migration preflight. A pure read-only openclaw status invocation does not mutate state when a QQBot backup is its only legacy marker; status does not need credential recovery. This avoids adding an external plugin's file names to the core config guard.

No config or environment-variable contract changes. QQBOT_DATA_DIR support remains separate work in #39461 / #78253.

Evidence

Automated validation

Real doctor migration proof

Ran the built CLI against an isolated HOME and state directory with one synthetic legacy credential backup:

openclaw doctor --fix --non-interactive --yes

Migrated 1 QQBot credential backup -> plugin state
Archived QQBot credential backup legacy source -> .../credential-backup-proof-account.json.migrated

Post-run verification:

source_archived=yes
archive_mode=600
sqlite_row=qqbot|credential-backups|<hashed-key>

No real credential or QQBot network call was used.

Compatibility / Migration

  • Backward compatible: yes, through the explicit doctor migration for persistent credential backups.
  • Config/env changes: no.
  • Upgrade step: run openclaw doctor --fix.
  • Changelog: not included; repository policy generates CHANGELOG.md during releases.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation cli CLI command changes channel: qqbot size: L maintainer Maintainer-authored PR labels Jun 2, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a05199de90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

stateDir: string;
}): CredentialBackupCandidate[] {
const roots = new Set<string>([path.join(params.stateDir, "qqbot", "data")]);
const home = params.env.HOME || params.env.USERPROFILE;

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 Badge Scan OS-home QQBot backups when HOME is unset

When OpenClaw runs with a custom OPENCLAW_HOME/OPENCLAW_STATE_DIR but without HOME or USERPROFILE exported, this only scans the active state dir and an env-derived home. The legacy runtime path this replaces used getQQBotDataPath()/getHomeDir(), which checks os.homedir() first, so existing credential backups under the real OS home are no longer migrated; since runtime now reads SQLite only, QQBot accounts with empty credentials will not be restored. Include the same OS-home resolver (or os.homedir()) in these candidate roots.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 2:59 PM ET / 18:59 UTC.

Summary
The PR moves QQBot credential-backup recovery from runtime JSON fallback into a plugin doctor SQLite KV migration, removes legacy runtime JSON/JSONL cache imports, and updates QQBot state tests and database-first docs.

PR surface: Source -49, Tests +100, Docs -1. Total +50 across 16 files.

Reproducibility: yes. Source inspection shows the PR removes runtime JSON fallback while ensureConfigReady still does not recognize QQBot credential-backup files as read-only migration inputs.

Review metrics: 2 noteworthy metrics.

  • Runtime legacy imports: 4 removed, 1 doctor-contract migration added. QQBot credential, session, known-user, and ref-index legacy JSON/JSONL runtime imports move to a stricter doctor-owned upgrade boundary.
  • Credential migration root: 1 active state root scanned. The latest branch avoids the earlier HOME-global credential scan, but the active-root marker still needs to be discoverable by read-only command preflight.

Stored data model
Persistent data-model change detected: database schema: docs/refactor/database-first.md, migration/backfill/repair: docs/refactor/database-first.md, migration/backfill/repair: extensions/qqbot/src/state-migrations.test.ts, migration/backfill/repair: extensions/qqbot/src/state-migrations.ts, persistent cache schema: extensions/qqbot/src/engine/session/session-store.test.ts, serialized state: extensions/qqbot/src/engine/ref/store.test.ts, and 12 more. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add a focused config-guard detector/test for qqbot/data/credential-backup*.json read-only migration preflight.
  • [P1] Add redacted terminal or log output from a real doctor/setup migration of a legacy QQBot credential backup.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body lists tests, Testbox, and autoreview, but no redacted terminal/log output from a real after-fix QQBot credential-backup doctor/setup migration. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P2] Existing QQBot legacy credential backups may not migrate during read-only commands such as openclaw status because the startup detector still does not recognize qqbot/data/credential-backup*.json after runtime fallback is removed.
  • [P1] The PR still has no redacted terminal or log proof from a real after-fix doctor/setup migration of a legacy QQBot credential backup.
  • [P1] The migration changes credential recovery and archives secret-bearing legacy files, so maintainers should explicitly review compatibility and state-root/security-boundary behavior before merge.

Maintainer options:

  1. Fix Read-Only Migration Detection (recommended)
    Before merge, add QQBot credential-backup markers to the config guard so read-only commands run the doctor path when only legacy QQBot backups exist.
  2. Accept Gateway/Doctor-Only Recovery
    Maintainers may decide that explicit doctor or gateway startup migration is enough, but that should be stated because it narrows the PR body's read-only migration claim.
  3. Hold For Real Credential Migration Proof
    Keep the PR blocked until the body includes redacted terminal or log output showing a legacy QQBot credential backup migrating into plugin KV and being archived.

Next step before merge

  • [P1] Human handling is needed because the PR has the protected maintainer label, lacks real migration proof, and the remaining credential preflight behavior needs maintainer-reviewed repair or acceptance.

Security
Cleared: No concrete new security or supply-chain defect was found in the latest diff, but the credential migration remains compatibility and proof sensitive.

Review findings

  • [P2] Trigger doctor preflight for QQBot backups — extensions/qqbot/src/engine/config/credential-backup.ts:75
Review details

Best possible solution:

Keep the SQLite-only runtime direction, add QQBot credential-backup markers to the read-only preflight detector with focused coverage, and require redacted real migration proof before maintainer approval.

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

Yes. Source inspection shows the PR removes runtime JSON fallback while ensureConfigReady still does not recognize QQBot credential-backup files as read-only migration inputs.

Is this the best way to solve the issue?

No. Moving imports out of runtime is the right boundary, but the current patch still needs read-only preflight detection and real migration proof before it is the safest merge path.

Full review comments:

  • [P2] Trigger doctor preflight for QQBot backups — extensions/qqbot/src/engine/config/credential-backup.ts:75
    After this change, loadCredentialBackup reads SQLite only, so legacy JSON backups must be migrated before recovery can work. The read-only config guard still only recognizes the existing bundled/channel markers and has no qqbot/data/credential-backup*.json marker, so openclaw status can skip the migration path the PR body says it should trigger when that backup is the only legacy state present.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5db7c3797de3.

Label changes

Label justifications:

  • P1: The PR changes QQBot credential recovery during upgrade and can affect users with existing appId/clientSecret backups.
  • merge-risk: 🚨 compatibility: Merging changes how shipped QQBot JSON credential backups are discovered, imported, and archived during upgrade.
  • merge-risk: 🚨 auth-provider: The migrated state contains QQBot appId/clientSecret recovery data that startup can use to restore live config.
  • merge-risk: 🚨 security-boundary: The migration touches credential backup state-root isolation and archives secret-bearing legacy files.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body lists tests, Testbox, and autoreview, but no redacted terminal/log output from a real after-fix QQBot credential-backup doctor/setup migration. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source -49, Tests +100, Docs -1. Total +50 across 16 files.

View PR surface stats
Area Files Added Removed Net
Source 9 300 349 -49
Tests 6 281 181 +100
Docs 1 5 6 -1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 16 586 536 +50

What I checked:

  • Root policy read: Read the full root AGENTS.md; its database-first, plugin-boundary, protected-label, proof, and compatibility-risk guidance affected this review. (AGENTS.md:1, 5db7c3797de3)
  • Scoped extension policy read: Read extensions/AGENTS.md; QQBot production code stays within the plugin boundary and uses plugin SDK doctor contracts rather than core internals. (extensions/AGENTS.md:1, 5db7c3797de3)
  • Runtime fallback removed: At the PR head, loadCredentialBackup reads SQLite only and says legacy JSON import belongs to doctor/setup migration, so any missed doctor path leaves legacy JSON backups unrecovered at runtime. (extensions/qqbot/src/engine/config/credential-backup.ts:75, e375f378e764)
  • Doctor contract wiring added: The latest branch exports QQBot stateMigrations from doctor-contract-api, which addresses the earlier packaged-plugin doctor-contract gap. (extensions/qqbot/doctor-contract-api.ts:2, e375f378e764)
  • Active-state migration implementation: The new state migration scans only the active stateDir's qqbot/data directory and archives migrated credential backup files into plugin state. (extensions/qqbot/src/state-migrations.ts:73, e375f378e764)
  • Read-only preflight detector gap: Current config guard only triggers read-only command migration for known legacy markers and does not include qqbot/data/credential-backup*.json, despite the PR relying on doctor migration after removing runtime fallback. (src/cli/program/config-guard.ts:122, 5db7c3797de3)

Likely related people:

  • steipete: Authored merged PR 89432, which moved QQBot credential backups, sessions, known users, and ref-index state into SQLite KV, and authored this follow-up branch. (role: recent QQBot state migration contributor; confidence: high; commits: 53371f79cf5e, 6467ddd7edf1, 4902c7e83ad7; files: extensions/qqbot/src/engine/config/credential-backup.ts, extensions/qqbot/src/engine/session/session-store.ts, extensions/qqbot/src/engine/ref/store.ts)
  • coygeek: Authored the merged QQBot credential-backup state isolation fix in PR 84314, which is the security boundary this follow-up must not regress. (role: state-root isolation fix author; confidence: high; commits: 4e781636ae79, 2e97f72a3dcd, a14be505ff29; files: extensions/qqbot/src/engine/utils/data-paths.ts, extensions/qqbot/src/engine/utils/data-paths.test.ts)
  • Sliverp: Git history shows the original QQBot channel and later QQBot channel fixes under this area, so they are useful routing context for QQBot-specific behavior. (role: original QQBot channel contributor; confidence: medium; commits: bf6f506dfae6, 65b781f9ae3a, ddb7a8dd80b8; files: extensions/qqbot)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added 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. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jun 2, 2026
@clawsweeper clawsweeper Bot mentioned this pull request Jun 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7a41d64f1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/qqbot/setup-entry.ts Outdated
Comment on lines +12 to +14
legacyStateMigrations: {
specifier: "./legacy-state-migrations-api.js",
exportName: "detectQQBotLegacyStateMigrations",

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 Badge Wire QQBot backup migration into external doctor path

This exposes the migration only through the bundled-channel setup-entry path, but the doctor collector calls listBundledChannelLegacyStateMigrationDetectors for these setup-entry migrations while external/plugin-installed migrations come from listPluginDoctorStateMigrationEntries; QQBot is also excluded from the core bundled dist (EXCLUDED_CORE_BUNDLED_PLUGIN_DIRS contains qqbot). In a packaged install where @openclaw/qqbot is installed externally, openclaw doctor --fix therefore never loads this detector, and since this commit removed the runtime JSON fallback, legacy qqbot/data/credential-backup*.json files are not imported and wiped credentials stay unrecovered. Add a QQBot doctor contract migration or teach the doctor migration collector to include external setup-entry detectors.

Useful? React with 👍 / 👎.

@steipete steipete self-assigned this Jul 1, 2026
@steipete
steipete force-pushed the fix/qqbot-cache-migration-cleanup branch from c7a41d6 to e375f37 Compare July 1, 2026 18:53
@openclaw-barnacle openclaw-barnacle Bot added size: XL and removed cli CLI command changes size: L labels Jul 1, 2026
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and repaired the migration boundary. The earlier ClawSweeper findings are addressed:

  • credential migration is exported through the external QQBot doctor-contract-api, not setup/core code;
  • legacy discovery is limited to the active state root;
  • failure/capacity rollback is scoped and concurrency-safe;
  • migrated secret-bearing sources are archived with mode 0600;
  • the PR body now contains real built-CLI doctor proof.

Land-ready proof on head e375f378e764b354eda2bfff0a03de96f4fdff44:

  • pnpm test extensions/qqbot -- --reporter=dot — 69 files, 597 tests passed.
  • pnpm build — passed.
  • Testbox tbx_01kwfg2v7tfn8371rwsbkw7c7f — scoped extension typechecks/tests, lint, database-first guard, docs, and import-cycle checks passed: https://github.com/openclaw/openclaw/actions/runs/28540114567
  • fresh branch autoreview with the full QQBot suite — no findings.
  • isolated openclaw doctor --fix --non-interactive --yes — migrated one synthetic credential backup into state/openclaw.sqlite, archived the source, and verified archive mode 0600.
  • git diff --check — passed.

Known proof gap: no live QQBot network call with real credentials; the migration proof used the real built doctor path with synthetic data.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Maintainer decision on the new read-only preflight finding: explicit doctor/Gateway recovery is the intended boundary; no core QQBot file marker should be added.

  • src/cli/program/config-guard.ts:149-162 limits lightweight file-marker gating to agent, status, and read-only task commands.
  • src/cli/program/config-guard.ts:215-221 already runs state migration preflight unconditionally for Gateway and other state-mutating commands.
  • src/commands/doctor-config-preflight.ts:199-228 invokes external plugin doctor migrations through autoMigrateLegacyPluginDoctorState.
  • extensions/qqbot/doctor-contract-api.ts:2 exports the QQBot migration through that external-plugin contract.

Therefore a pure openclaw status does not migrate when a QQBot backup is its only marker; it does not need credentials. Upgrade installers, explicit openclaw doctor --fix, Gateway startup, and state-mutating commands do migrate. Adding qqbot/data/credential-backup*.json to core would violate the plugin-agnostic owner boundary for an external plugin.

The PR body now states this trigger boundary explicitly. It already includes redacted real built-CLI proof:

Migrated 1 QQBot credential backup -> plugin state
Archived QQBot credential backup legacy source -> .../credential-backup-proof-account.json.migrated
source_archived=yes
archive_mode=600
sqlite_row=qqbot|credential-backups|<hashed-key>

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@steipete
steipete merged commit 24d94a5 into main Jul 1, 2026
114 of 117 checks passed
@steipete
steipete deleted the fix/qqbot-cache-migration-cleanup branch July 1, 2026 19:04
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

liuhao1024 pushed a commit to liuhao1024/openclaw that referenced this pull request Jul 1, 2026
* fix: migrate qqbot credential backups to sqlite kv

* fix: complete qqbot sqlite migration cleanup

* fix: harden qqbot doctor state migration
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 2, 2026
* fix: migrate qqbot credential backups to sqlite kv

* fix: complete qqbot sqlite migration cleanup

* fix: harden qqbot doctor state migration
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
* fix: migrate qqbot credential backups to sqlite kv

* fix: complete qqbot sqlite migration cleanup

* fix: harden qqbot doctor state migration
sheyanmin pushed a commit to sheyanmin/openclaw that referenced this pull request Jul 8, 2026
* fix: migrate qqbot credential backups to sqlite kv

* fix: complete qqbot sqlite migration cleanup

* fix: harden qqbot doctor state migration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: qqbot docs Improvements or additions to documentation maintainer Maintainer-authored PR merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant