fix(doctor): commit legacy migrations even when unrelated validation fails#76800
Conversation
|
Codex review: passed. Summary Reproducibility: yes. Source inspection on current main shows Next step before merge Security Review detailsBest possible solution: Doctor should persist safe legacy migration output and use an internal write path that skips only plugin-aware validation when unrelated plugin issues remain. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection on current main shows Is this the best way to solve the issue? Yes. The PR is the narrow maintainable fix: mark the migration as partially valid, keep the migrated candidate, and skip only plugin-aware write validation while preserving base schema validation. What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 7915110259c2. |
|
Fixed. Pushed fixup Changes in this fixup:
Tests: |
|
Fixup pushed (SHA `8acad8f93d`): P2 — exported writeConfigFile wrapper now forwards skipPluginValidation P2 — include-write fast path now skips plugin validation when flag is set Regression test added ( Tests: 59/59 pass ( Re-review progress:
|
|
Fixup pushed (SHA `023a7ceae8`): P2 — include fast path bails out when skipPluginValidation is set Tests: 65/65 pass. Formatter clean. |
|
Thanks, this is the right direction for #76798: safe legacy doctor migrations should not be dropped just because an unrelated plugin/provider validation issue remains. Current blocker is mechanical rather than conceptual: GitHub reports this branch as conflicting against current |
023a7ce to
3769ae9
Compare
…nclaw#76800) Clawsweeper P2 x2: 1. io.ts exported writeConfigFile wrapper now passes skipPluginValidation to createConfigIO so both write-phase validation and post-write loadConfig re-read honor the flag. 2. mutate.ts tryWriteSingleTopLevelIncludeMutation now skips plugin validation when writeOptions.skipPluginValidation is set, so include-write fast path no longer blocks safe legacy migrations with unrelated plugin errors. Adds regression test: skipPluginValidation bypasses plugin schema rejection on writeConfigFile and falls back to throwing when flag is not set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n is set (openclaw#76800) Clawsweeper P2: after the include write, readConfigFileSnapshotForWrite() calls loadConfig() which validates with plugins; refreshedSnapshot.valid is false when an unrelated plugin issue exists, causing the include path to throw even though skipPluginValidation was requested. Simplest fix: return false from tryWriteSingleTopLevelIncludeMutation when skipPluginValidation is set, letting the root writer handle the write with plugin validation disabled end-to-end (including post-write readback via createConfigIO({ pluginValidation: "skip" })). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Rebased onto current main — merge conflict in CHANGELOG.md resolved. CI running. |
|
Fixed lint failure: |
…fails (openclaw#76798) migrateLegacyConfig previously returned config: null when post-migration validation found any issue (e.g. a missing plugin). The caller then kept the unmigrated config as the candidate, so doctor --fix never wrote the legacy migration to disk. Now when validation fails after a successful migration, the migrated config is returned with partiallyValid: true. applyLegacyCompatibilityStep always commits the migrated config to state.candidate, ensuring agents.defaults.llm and other known-legacy keys are cleaned up on doctor --fix even when an unrelated provider or plugin issue blocks the full validator. Adds regression test asserting that candidate is updated to the migrated shape when partiallyValid is set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread skipPluginValidationOnWrite through loadAndMaybeMigrateDoctorConfig return value into runWriteConfigHealth so replaceConfigFile bypasses plugin validation when migration is only partially valid. Add E2E test verifying the flag propagates end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nclaw#76800) Clawsweeper P2 x2: 1. io.ts exported writeConfigFile wrapper now passes skipPluginValidation to createConfigIO so both write-phase validation and post-write loadConfig re-read honor the flag. 2. mutate.ts tryWriteSingleTopLevelIncludeMutation now skips plugin validation when writeOptions.skipPluginValidation is set, so include-write fast path no longer blocks safe legacy migrations with unrelated plugin errors. Adds regression test: skipPluginValidation bypasses plugin schema rejection on writeConfigFile and falls back to throwing when flag is not set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n is set (openclaw#76800) Clawsweeper P2: after the include write, readConfigFileSnapshotForWrite() calls loadConfig() which validates with plugins; refreshedSnapshot.valid is false when an unrelated plugin issue exists, causing the include path to throw even though skipPluginValidation was requested. Simplest fix: return false from tryWriteSingleTopLevelIncludeMutation when skipPluginValidation is set, letting the root writer handle the write with plugin validation disabled end-to-end (including post-write readback via createConfigIO({ pluginValidation: "skip" })). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4ae444b to
e4490be
Compare
|
@clawsweeper automerge |
|
🦞👀 Source: I left the PR open for the remaining gate instead of bypassing it. Automerge progress:
|
#76800 (fixes #76798) added user-facing `doctor --fix` behavior to commit safe legacy migrations even when unrelated validation issues prevent full validation, but the existing entry credited no contributor and used a bare PR/issue reference. Promote #76798 to a `Fixes` ref, add the merging PR ref (#76800), and credit the human contributor @hclsys per CLAUDE.md changelog-attribution rules.
openclaw#76800 (fixes openclaw#76798) added user-facing `doctor --fix` behavior to commit safe legacy migrations even when unrelated validation issues prevent full validation, but the existing entry credited no contributor and used a bare PR/issue reference. Promote openclaw#76798 to a `Fixes` ref, add the merging PR ref (openclaw#76800), and credit the human contributor @hclsys per CLAUDE.md changelog-attribution rules.
…fails (openclaw#76800) * fix(doctor): commit legacy migrations even when unrelated validation fails (openclaw#76798) migrateLegacyConfig previously returned config: null when post-migration validation found any issue (e.g. a missing plugin). The caller then kept the unmigrated config as the candidate, so doctor --fix never wrote the legacy migration to disk. Now when validation fails after a successful migration, the migrated config is returned with partiallyValid: true. applyLegacyCompatibilityStep always commits the migrated config to state.candidate, ensuring agents.defaults.llm and other known-legacy keys are cleaned up on doctor --fix even when an unrelated provider or plugin issue blocks the full validator. Adds regression test asserting that candidate is updated to the migrated shape when partiallyValid is set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup: extend skipPluginValidation to write path with E2E coverage Thread skipPluginValidationOnWrite through loadAndMaybeMigrateDoctorConfig return value into runWriteConfigHealth so replaceConfigFile bypasses plugin validation when migration is only partially valid. Add E2E test verifying the flag propagates end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup(doctor): wire skipPluginValidation through full write path (openclaw#76800) Clawsweeper P2 x2: 1. io.ts exported writeConfigFile wrapper now passes skipPluginValidation to createConfigIO so both write-phase validation and post-write loadConfig re-read honor the flag. 2. mutate.ts tryWriteSingleTopLevelIncludeMutation now skips plugin validation when writeOptions.skipPluginValidation is set, so include-write fast path no longer blocks safe legacy migrations with unrelated plugin errors. Adds regression test: skipPluginValidation bypasses plugin schema rejection on writeConfigFile and falls back to throwing when flag is not set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup(doctor): bail out of include fast path when skipPluginValidation is set (openclaw#76800) Clawsweeper P2: after the include write, readConfigFileSnapshotForWrite() calls loadConfig() which validates with plugins; refreshedSnapshot.valid is false when an unrelated plugin issue exists, causing the include path to throw even though skipPluginValidation was requested. Simplest fix: return false from tryWriteSingleTopLevelIncludeMutation when skipPluginValidation is set, letting the root writer handle the write with plugin validation disabled end-to-end (including post-write readback via createConfigIO({ pluginValidation: "skip" })). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(doctor): cover partial legacy migration writes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
openclaw#76800 (fixes openclaw#76798) added user-facing `doctor --fix` behavior to commit safe legacy migrations even when unrelated validation issues prevent full validation, but the existing entry credited no contributor and used a bare PR/issue reference. Promote openclaw#76798 to a `Fixes` ref, add the merging PR ref (openclaw#76800), and credit the human contributor @hclsys per CLAUDE.md changelog-attribution rules.
…fails (openclaw#76800) * fix(doctor): commit legacy migrations even when unrelated validation fails (openclaw#76798) migrateLegacyConfig previously returned config: null when post-migration validation found any issue (e.g. a missing plugin). The caller then kept the unmigrated config as the candidate, so doctor --fix never wrote the legacy migration to disk. Now when validation fails after a successful migration, the migrated config is returned with partiallyValid: true. applyLegacyCompatibilityStep always commits the migrated config to state.candidate, ensuring agents.defaults.llm and other known-legacy keys are cleaned up on doctor --fix even when an unrelated provider or plugin issue blocks the full validator. Adds regression test asserting that candidate is updated to the migrated shape when partiallyValid is set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup: extend skipPluginValidation to write path with E2E coverage Thread skipPluginValidationOnWrite through loadAndMaybeMigrateDoctorConfig return value into runWriteConfigHealth so replaceConfigFile bypasses plugin validation when migration is only partially valid. Add E2E test verifying the flag propagates end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup(doctor): wire skipPluginValidation through full write path (openclaw#76800) Clawsweeper P2 x2: 1. io.ts exported writeConfigFile wrapper now passes skipPluginValidation to createConfigIO so both write-phase validation and post-write loadConfig re-read honor the flag. 2. mutate.ts tryWriteSingleTopLevelIncludeMutation now skips plugin validation when writeOptions.skipPluginValidation is set, so include-write fast path no longer blocks safe legacy migrations with unrelated plugin errors. Adds regression test: skipPluginValidation bypasses plugin schema rejection on writeConfigFile and falls back to throwing when flag is not set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fixup(doctor): bail out of include fast path when skipPluginValidation is set (openclaw#76800) Clawsweeper P2: after the include write, readConfigFileSnapshotForWrite() calls loadConfig() which validates with plugins; refreshedSnapshot.valid is false when an unrelated plugin issue exists, causing the include path to throw even though skipPluginValidation was requested. Simplest fix: return false from tryWriteSingleTopLevelIncludeMutation when skipPluginValidation is set, letting the root writer handle the write with plugin validation disabled end-to-end (including post-write readback via createConfigIO({ pluginValidation: "skip" })). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(doctor): cover partial legacy migration writes --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
openclaw#76800 (fixes openclaw#76798) added user-facing `doctor --fix` behavior to commit safe legacy migrations even when unrelated validation issues prevent full validation, but the existing entry credited no contributor and used a bare PR/issue reference. Promote openclaw#76798 to a `Fixes` ref, add the merging PR ref (openclaw#76800), and credit the human contributor @hclsys per CLAUDE.md changelog-attribution rules.
Fixes #76798.
Problem
migrateLegacyConfigreturnedconfig: nullwhen post-migration validation found any unrelated issue (e.g. a missing plugin, a broken provider). The caller (applyLegacyCompatibilityStep) then kept the unmigrated config as the candidate, sodoctor --fixnever wrote the migration to disk.Concrete repro: user has both
agents.defaults.llm.idleTimeoutSeconds(deprecated legacy key) and a stale brave plugin entry.doctor --fixknows how to migrate thellmkey but drops it on the floor because the brave plugin fails validation — leaving users stuck in the LKG-restore loop documented in #76700.Fix
When
applyLegacyDoctorMigrationssucceeds butvalidateConfigObjectWithPluginsfails on the result, returnconfig: next(the migrated shape) withpartiallyValid: trueinstead ofconfig: null.applyLegacyCompatibilityStepalready handles this correctly — the!migratedbranch was the only blocker. Now the migrated config is committed tostate.candidateregardless of unrelated validation issues, sodoctor --fixalways applies every safe migration it knows about.Tests
Regression test added to
config-flow-steps.test.ts: asserts that whenmigrateLegacyConfigreturnspartiallyValid: true,state.candidateandstate.cfgare updated to the migrated shape.🤖 Generated with Claude Code