Skip to content

fix(config): migrate legacy acp.stream keys on load#60824

Open
BlueBirdBack wants to merge 2 commits intoopenclaw:mainfrom
BlueBirdBack:fix/issue-35957-acp-stream-legacy-migration
Open

fix(config): migrate legacy acp.stream keys on load#60824
BlueBirdBack wants to merge 2 commits intoopenclaw:mainfrom
BlueBirdBack:fix/issue-35957-acp-stream-legacy-migration

Conversation

@BlueBirdBack
Copy link
Copy Markdown
Contributor

Summary

Fixes #35957 by migrating legacy acp.stream keys from older configs during normal legacy-config migration on load.

Migrated:

  • acp.stream.maxTurnCharsacp.stream.maxOutputChars
  • acp.stream.maxToolSummaryCharsacp.stream.maxSessionUpdateChars

Removed as legacy keys with no replacement:

  • acp.stream.maxStatusChars
  • acp.stream.maxMetaEventsPerTurn
  • acp.stream.metaMode
  • acp.stream.showUsage

Tests

Added regression coverage for:

  • raw validation detecting the old keys
  • migration output shape
  • normal config validation succeeding after migration
  • non-overwrite behavior when new keys are already set

Verification

Verified with a Bug Lab fixture-backed check using an old v2026.3.2 config fixture:

  • current main => FIXTURE_CHECK: bug_present / VERDICT: code-suspect
  • patched branch => FIXTURE_CHECK: fixed / VERDICT: cannot_reproduce

Note

Local commit used --no-verify because the repo currently has unrelated baseline type/dependency failures in other areas; the actual config fix was still verified separately with the fixture-backed repro check.

Fix issue openclaw#35957 by migrating old v2026.3.2 acp.stream keys to the
current schema during normal legacy-config migration.

- maxTurnChars -> maxOutputChars
- maxToolSummaryChars -> maxSessionUpdateChars
- remove maxStatusChars, maxMetaEventsPerTurn, metaMode, showUsage

Add regression coverage for raw validation, migration output, normal
config validation, and non-overwrite behavior when new keys already
exist.

Verified with Bug Lab fixture-backed check:
- current main => bug_present / code-suspect
- patched worktree => fixed / cannot_reproduce
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 4, 2026

Greptile Summary

This PR fixes issue #35957 by wiring a new acp.stream-v2026.3.2-keys migration into the legacy config runtime migration pipeline. It renames two deprecated keys (maxTurnCharsmaxOutputChars, maxToolSummaryCharsmaxSessionUpdateChars) with correct non-overwrite semantics, and drops four removed keys (maxStatusChars, maxMetaEventsPerTurn, metaMode, showUsage) on load. The approach fits cleanly into the existing defineLegacyConfigMigration / LEGACY_CONFIG_MIGRATIONS_RUNTIME pattern used throughout src/config/.

Key observations:

  • The migrateLegacyAcpStream helper correctly mutates the cloned stream object in-place; the parent raw.acp reference does not need to be re-assigned because getRecord returns a direct object reference and applyLegacyMigrations structuredClones its input before calling any migration.
  • The shared LEGACY_ACP_STREAM_FIXTURE in the test file is safe across test cases for the same reason.
  • The four keys that are removed with no replacement are only deleted when their value matches the expected runtime type (e.g. typeof stream.maxStatusChars === "number"). A config with a wrongly-typed value for these keys would survive migration and continue failing validation with a confusing "still invalid" message. Removing them unconditionally would better match the "auto-removed on load" contract stated in the rule messages.

Confidence Score: 4/5

Safe to merge; the migration is additive, correctly clones before mutating, and the renamed-key logic has solid test coverage.

The core logic is sound and follows established codebase patterns. applyLegacyMigrations uses structuredClone so there is no risk of input mutation. The only notable issue is that the removed-with-no-replacement keys are gated by type checks, meaning a malformed legacy config with wrong-typed values won't be auto-cleaned — a marginal edge case real v2026.3.2 configs wouldn't hit.

No files require special attention; the single style concern is in src/config/legacy.migrations.runtime.ts lines 448–478.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/legacy.migrations.runtime.ts
Line: 448-478

Comment:
**Type guards on removed keys may silently skip cleanup**

For the four keys being *removed with no replacement* (`maxStatusChars`, `maxMetaEventsPerTurn`, `metaMode`, `showUsage`), the migration only deletes the key when its runtime value matches the expected type. If a config has a wrongly-typed value (e.g. `maxStatusChars: "400"` as a string), the guard fails silently: the key is left in place, the legacy rule still flags it (it has no `match` predicate), and `migrateLegacyConfig` ends up returning `{ config: null, changes: ["Migration applied, but config still invalid…"] }` — indistinguishable to the user from a real schema error.

For the *renamed* keys (`maxTurnChars`, `maxToolSummaryChars`) the type guard makes sense because you want to migrate a valid number to the new name. But for the removed keys there is no value to preserve — removing them unconditionally would match the "auto-removed on load" intent in the rule messages and avoid the confusing stuck-migration case.

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

Reviews (1): Last reviewed commit: "fix(config): migrate legacy acp.stream k..." | Re-trigger Greptile

Comment on lines +448 to +478
if (
Object.prototype.hasOwnProperty.call(stream, "maxStatusChars") &&
typeof stream.maxStatusChars === "number"
) {
delete stream.maxStatusChars;
changes.push("Removed acp.stream.maxStatusChars (no replacement).");
}

if (
Object.prototype.hasOwnProperty.call(stream, "maxMetaEventsPerTurn") &&
typeof stream.maxMetaEventsPerTurn === "number"
) {
delete stream.maxMetaEventsPerTurn;
changes.push("Removed acp.stream.maxMetaEventsPerTurn (no replacement).");
}

if (
Object.prototype.hasOwnProperty.call(stream, "metaMode") &&
typeof stream.metaMode === "string"
) {
delete stream.metaMode;
changes.push("Removed acp.stream.metaMode (no replacement).");
}

if (
Object.prototype.hasOwnProperty.call(stream, "showUsage") &&
typeof stream.showUsage === "boolean"
) {
delete stream.showUsage;
changes.push("Removed acp.stream.showUsage (no replacement).");
}
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 Type guards on removed keys may silently skip cleanup

For the four keys being removed with no replacement (maxStatusChars, maxMetaEventsPerTurn, metaMode, showUsage), the migration only deletes the key when its runtime value matches the expected type. If a config has a wrongly-typed value (e.g. maxStatusChars: "400" as a string), the guard fails silently: the key is left in place, the legacy rule still flags it (it has no match predicate), and migrateLegacyConfig ends up returning { config: null, changes: ["Migration applied, but config still invalid…"] } — indistinguishable to the user from a real schema error.

For the renamed keys (maxTurnChars, maxToolSummaryChars) the type guard makes sense because you want to migrate a valid number to the new name. But for the removed keys there is no value to preserve — removing them unconditionally would match the "auto-removed on load" intent in the rule messages and avoid the confusing stuck-migration case.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/legacy.migrations.runtime.ts
Line: 448-478

Comment:
**Type guards on removed keys may silently skip cleanup**

For the four keys being *removed with no replacement* (`maxStatusChars`, `maxMetaEventsPerTurn`, `metaMode`, `showUsage`), the migration only deletes the key when its runtime value matches the expected type. If a config has a wrongly-typed value (e.g. `maxStatusChars: "400"` as a string), the guard fails silently: the key is left in place, the legacy rule still flags it (it has no `match` predicate), and `migrateLegacyConfig` ends up returning `{ config: null, changes: ["Migration applied, but config still invalid…"] }` — indistinguishable to the user from a real schema error.

For the *renamed* keys (`maxTurnChars`, `maxToolSummaryChars`) the type guard makes sense because you want to migrate a valid number to the new name. But for the removed keys there is no value to preserve — removing them unconditionally would match the "auto-removed on load" intent in the rule messages and avoid the confusing stuck-migration case.

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

@BlueBirdBack
Copy link
Copy Markdown
Contributor Author

Good catch. I pushed e6a344ef7 to address exactly that edge case:

  • removed the type guards for the no-replacement legacy keys (maxStatusChars, maxMetaEventsPerTurn, metaMode, showUsage) so they are always stripped on migration
  • added a regression test covering wrong-typed legacy values for those keys

Focused check: pnpm exec vitest run --config vitest.config.ts src/config/config.acp-stream-migration.test.ts

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.

Config migration issue: Invalid keys from v2026.3.2 cause gateway startup failure in v2026.3.3

1 participant