Skip to content

fix(onboard): preserve agents.list and bindings on rerun (#84692)#84748

Merged
steipete merged 9 commits into
openclaw:mainfrom
yetval:fix/onboard-preserve-agents-bindings-84692
May 27, 2026
Merged

fix(onboard): preserve agents.list and bindings on rerun (#84692)#84748
steipete merged 9 commits into
openclaw:mainfrom
yetval:fix/onboard-preserve-agents-bindings-84692

Conversation

@yetval

@yetval yetval commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #84692 — re-running openclaw onboard on an existing installation silently discards agents.list and bindings, collapsing the on-disk config from ~9.9 KB to ~1.8 KB. The live gateway keeps serving from in-memory config so the breakage is invisible until the next restart, at which point every account bound to a now-missing agent misroutes. The config-audit log already flags the write as size-drop:9958->1783 but the write is committed anyway.

Two compounding root causes:

  1. The size-drop guard is bypassed on every ordinary onboard. All three onboard writers — interactive (writeWizardConfigFile in src/wizard/setup.ts), non-interactive local (src/commands/onboard-non-interactive/local.ts), and non-interactive remote (src/commands/onboard-non-interactive/remote.ts) — hardcode allowConfigSizeDrop: true. The generic config-write guard at src/config/io.ts:resolveConfigWriteBlockingReasons only blocks size-drop:* when that flag is absent, so the audit log warns but never aborts.
  2. There is no load-time validation of bindings[].agentId against agents.list. The broadcast superRefine already validates dangling agent ids; bindings did not. If the destructive write slips through, the next gateway restart loads it without complaint and the misroute surfaces silently.

This change gates the size-drop bypass behind explicit reset/import flows in every onboard writer and makes a dangling bindings[].agentId a load-time schema rejection. Either layer alone now blocks the reported failure, and they reinforce each other.

What changed

  • src/wizard/setup.ts
    • writeWizardConfigFile(config, opts?) now takes an allowConfigSizeDrop parameter that defaults to false. The hardcoded true is gone.
    • A new configResetPerformed boolean tracks whether the user picked the reset action (or the wizard threw the config away via baseConfig = {}). Every onboard write threads that boolean into the new opts.
    • The migration-import path passes allowConfigSizeDrop: true explicitly because importing legitimately shrinks the running config.
  • src/commands/onboard-non-interactive/local.ts
    • allowConfigSizeDrop is now opts.reset === true instead of hardcoded true.
  • src/commands/onboard-non-interactive/remote.ts
    • Same fix for the --mode remote writer; this was missed in the first commit and surfaced by the ClawSweeper review on this PR.
  • src/config/zod-schema.ts
    • The top-level OpenClawConfigSchema.superRefine now walks cfg.bindings and emits a custom issue (Unknown agent id "<id>" (not in agents.list).) for every bindings[i].agentId missing from cfg.agents.list. Gated on agents.list.length > 0 so legacy configs that have not populated agents.list yet still validate.
  • src/commands/onboard-config.test.ts — new case: applyLocalSetupWorkspaceConfig on a config with seeded agents.list + bindings preserves both arrays verbatim across a rerun.
  • src/commands/onboard-non-interactive.gateway.test.tsreplaceConfigFile mock now captures writeOptions for assertions. New cases for both local and remote: runNonInteractiveSetup on a seeded config preserves both agents.list ids and the full bindings array, and the last on-disk write is committed with allowConfigSizeDrop: false.
  • src/config/config.schema-regressions.test.ts — three new cases: dangling bindings[].agentId is rejected with the expected message; the same shape with a valid agentId validates; bindings against an empty agents.list still pass (legacy passthrough).
  • src/wizard/setup.test.ts — existing test that asserted allowConfigSizeDrop: true for the interactive wizard now asserts false, because that case starts from a fresh config (no reset performed) and the new safe default is false.

Real behavior proof

Behavior addressed: ordinary openclaw onboard rerun silently empties agents.list and bindings; the next gateway restart loads the stripped config without complaint and accounts misroute. (#84692)

Real environment tested: macOS arm64 (Darwin 25.3.0), Node v25.9.0. Real fs (node:fs/promises) under a mktemp -d HOME. Real production OpenClawConfigSchema (no schema mock), real replaceConfigFile from src/config/mutate.ts (no fs mock), and real node openclaw.mjs onboard --non-interactive ... CLI invocations against the same tmp HOME. Repro script proof-84692.ts runs via the project's own tsx (node_modules/.bin/tsx proof-84692.ts), and the CLI is run from the built dist/. Two worktrees were used so the same seeded config could be exercised against the patched branch and against upstream main (commit 5c4c6a42) under identical conditions.

Exact steps or command run after this patch:

  # Patched branch
git checkout fix/onboard-preserve-agents-bindings-84692
pnpm install --frozen-lockfile

  # Direct schema + io-guard proof (real schema, real replaceConfigFile, real fs)
node_modules/.bin/tsx proof-84692.ts

  # Real CLI proof: seed a populated config, then rerun onboard non-interactively
TMP_HOME=$(mktemp -d)
cat > $TMP_HOME/openclaw.json <<EOF   # 8 agents (alpha..theta), 8 bindings
{ "agents": { "list": [ { "id": "alpha", ... }, ..., { "id": "theta", ... } ] },
  "bindings": [ { "type": "route", "agentId": "alpha", ... }, ..., { "type": "route", "agentId": "theta", ... } ],
  "gateway": { "mode": "local", "port": 18789, "auth": { "mode": "token", "token": "seed_tok_xyz_redacted" } }
}
EOF

OPENCLAW_STATE_DIR=$TMP_HOME HOME=$TMP_HOME \
  OPENCLAW_SKIP_CHANNELS=1 OPENCLAW_SKIP_GMAIL_WATCHER=1 OPENCLAW_SKIP_CRON=1 \
  OPENCLAW_SKIP_CANVAS_HOST=1 OPENCLAW_SKIP_BROWSER_CONTROL_SERVER=1 \
  node openclaw.mjs onboard --non-interactive --accept-risk --mode local \
    --auth-choice skip --workspace $TMP_HOME/ws --gateway-bind loopback \
    --gateway-auth token --gateway-token seed_tok_xyz_redacted \
    --skip-skills --skip-health

Evidence after fix: live terminal output from tsx proof-84692.ts on the patched branch. The script exercises the real OpenClawConfigSchema and the real replaceConfigFile (no mocks) against a tmp openclaw.json and prints the result of five scenarios:

[trace] seeded /var/folders/xd/.../proof-84692-dOv3em/openclaw.json (911 bytes)

[scenario 1] valid populated config -> validateConfigObject
  ok=true

[scenario 2] binding with missing agentId='ghost' -> reject
  ok=false
  issue.path: bindings.0.agentId
  issue.message: Unknown agent id "ghost" (not in agents.list).

[scenario 3] legacy passthrough: bindings without agents.list
  ok=true

[scenario 4] ordinary-onboard shrink rejected on real fs
Config write rejected: /var/folders/xd/.../openclaw.json (size-drop:911->165). Rejected payload saved to /var/folders/xd/.../openclaw.json.rejected.2026-05-21T01-20-56-810Z.
  rejected=true
  message=Config write rejected: ... (size-drop:911->165). Rejected payload saved to ...
  on-disk bytes before=911 after=911

[scenario 5] same shrink with allowConfigSizeDrop=true (pre-patch onboard behavior)
Config write anomaly: /var/folders/xd/.../openclaw.json (size-drop:911->165)
Config observe anomaly: /var/folders/xd/.../openclaw.json (size-drop-vs-last-good:911->165)
  on-disk bytes before=911 after=165
  data loss reproduced: -746 bytes
  parsed: agents.list=missing bindings=missing

OK: openclaw#84692 fix verified end-to-end on real fs + real schema

Scenario 5 is the issue reporter's exact failure mode replayed on real fs: with allowConfigSizeDrop: true (the value every ordinary onboard write was hardcoding before this PR), the populated 911-byte file is overwritten with the 165-byte shrunk payload, agents.list and bindings are no longer parseable from the on-disk JSON, and the same size-drop:911->165 and size-drop-vs-last-good:911->165 lines appear in the audit log — matching the issue's size-drop:9958->1783 and size-drop-vs-last-good:9958->1783, just at a smaller scale. Scenario 4 is what this PR changes that to: the io guard now aborts the same write, dumps the rejected payload to a .rejected.<ISO>.json sidecar, and the on-disk bytes do not move (before=911 after=911).

Real CLI rerun against the patched branch with the same seeded 8-agent / 8-binding config (full transcript captured locally, paths redacted to ~):

$ stat -f %z ~/openclaw.json                                        # before
1737
$ jq -c '[ .agents.list | length, .bindings | length ]' ~/openclaw.json
[8,8]
$ jq -c '.agents.list | map(.id)' ~/openclaw.json
["alpha","beta","gamma","delta","epsilon","zeta","eta","theta"]

$ OPENCLAW_STATE_DIR=~ HOME=~ OPENCLAW_SKIP_CHANNELS=1 ... \
    node openclaw.mjs onboard --non-interactive --accept-risk --mode local \
    --auth-choice skip --workspace ~/ws --gateway-bind loopback \
    --gateway-auth token --gateway-token seed_tok_xyz_redacted \
    --skip-skills --skip-health
Config write anomaly: ~/openclaw.json (missing-meta-before-write)
Updated config: ~/openclaw.json
  Backup: ~/openclaw.json.bak
Workspace OK: ~/ws
Sessions OK: ~/agents/main/sessions

$ stat -f %z ~/openclaw.json                                        # after
2970
$ jq -c '[ .agents.list | length, .bindings | length ]' ~/openclaw.json
[8,8]
$ jq -c '.agents.list | map(.id)' ~/openclaw.json
["alpha","beta","gamma","delta","epsilon","zeta","eta","theta"]

$ tail -1 ~/logs/config-audit.jsonl | jq -c '{event,previousBytes,nextBytes,suspicious,result}'
{"event":"config.write","previousBytes":1737,"nextBytes":2970,"suspicious":["missing-meta-before-write"],"result":"rename"}

previousBytes=1737 nextBytes=2970 is the inverse shape of the bug: the on-disk config grew (wizard added defaults), not shrank. The audit log contains no size-drop:* entry, only the cosmetic missing-meta-before-write advisory (the seed had no meta block). All eight agent ids and all eight bindings survive the rerun byte-for-byte; this is the maintainer-visible end state if a user with the reported issue runs openclaw onboard again on top of the patch.

The same --non-interactive --mode local rerun was also exercised against upstream main (worktree at 5c4c6a42) with the same seed; both paths preserve the arrays end-to-end through the non-interactive composition. The original report's destructive write therefore comes from a different onboard path (most likely the interactive wizard or a downstream sub-setup that reconstructs agents/bindings), and pinning the exact zeroing site needs a runtime repro of the reporter's setup that this read-only review did not have. The defense-in-depth shape of this patch — io guard at write time, schema rejection at load time — blocks the failure mode regardless of which onboard composition step zeroed the arrays (matches the clawsweeper:needs-live-repro advisory on the issue).

Observed result after fix:

  • Scenario 1: populated config (2 agents, 2 bindings) validates clean (ok=true).
  • Scenario 2: one bindings[].agentId replaced with "ghost" is now rejected at bindings.0.agentId with Unknown agent id "ghost" (not in agents.list).. On main the same shape returns ok=true, which is the silent-misroute path the reporter saw after a gateway restart.
  • Scenario 3: legacy passthrough preserved — same dangling binding against an empty agents.list still validates.
  • Scenario 4: a default-mode (allowConfigSizeDrop: false) shrink from 911 bytes to 165 bytes is rejected with size-drop:911->165; rejected payload is dumped to openclaw.json.rejected.<ISO>.json; on-disk openclaw.json is unchanged (before=911 after=911).
  • Scenario 5: the same shrink with allowConfigSizeDrop: true (pre-patch behavior) commits — file collapses 911 → 165 bytes, agents.list and bindings no longer parseable. This is the reporter's bug, reproduced byte-for-byte on real fs.
  • Real CLI rerun on the patched branch: 1737 → 2970 bytes (grew), 8 agents preserved, 8 bindings preserved, audit log shows no size-drop.

What was not tested:

  • The exact end-to-end interactive openclaw onboard wizard rerun that wiped the reporter's config in the original ticket was not exercised: the interactive wizard prompts for input that can't be supplied non-interactively, and the destructive composition step inside the wizard sub-setups (channels, gateway, skills, hooks, official plugins, plugin-config) was not pinned in this read-only review. Both the io-guard path and the schema-load path are exercised against the real production code, so the safety nets reported in the bug are now active regardless of which wizard sub-setup zeros the arrays. Matches the clawsweeper:needs-live-repro advisory on the issue.
  • Post-restart gateway routing behavior with a stripped config was not exercised end-to-end. The dangling bindings[].agentId test covers the load-time rejection that would now block a stripped config from booting silently into the gateway; an actual restart with the new fail-loud schema would either be rejected at config-load (test scenario 2) or load with agents.list populated (the only path that produces a valid config).

Supplemental: targeted vitest suite passes locally — node scripts/run-vitest.mjs run src/wizard/setup.test.ts src/commands/onboard-non-interactive.gateway.test.ts src/commands/onboard-config.test.ts src/config/io.write-prepare.test.ts src/config/config.schema-regressions.test.ts src/config/validation.allowed-values.test.tsTest Files 6 passed (6), Tests 105 passed (105), Duration 1.2s (gains one from the remote-rerun regression case added in the follow-up commit). src/config/io.write-config.test.ts > config io write > keeps shipped plugin install config records when index migration fails is a pre-existing flake on upstream main (reproduces on 5c4c6a42 with the patch removed); not caused by this change.

Notes

  • The dangling-binding superRefine is intentionally one-directional: it does not reject when agents.list is empty/undefined. That matches the clawsweeper review caveat ("Strict dangling-binding validation should be added only in a compatible way") and avoids breaking configs mid-migration that have bindings populated before agents.list.
  • The size-drop audit log line is unchanged. The change is that the write is now actually rejected when the bypass is not set, so the next gateway start cannot load a config that was emptied by a buggy onboard path.

Closes #84692

@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 21, 2026
@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 27, 2026, 5:42 AM ET / 09:42 UTC.

Summary
The PR gates onboard config size-drop writes to explicit reset/import/plugin-migration flows, adds binding-to-agent validation plus doctor pruning for dangling bindings, and extends onboarding/config regression coverage.

PR surface: Source +92, Tests +341, Other 0. Total +433 across 13 files.

Reproducibility: Do we have a high-confidence way to reproduce the issue? Yes for the core size-drop bypass and dangling-binding misroute path from current-main source plus the PR's terminal proof, but this read-only review did not rerun the exact reporter interactive setup that emptied the arrays.

Review metrics: 1 noteworthy metric.

  • Config safety surface: 2 changed, 1 repair added. Onboard write defaults, load-time binding validation, and doctor pruning affect upgrade behavior and need maintainer compatibility review before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

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

Rank-up moves:

  • Maintainer should explicitly approve the fail-closed binding validation and doctor pruning behavior before merge.

Risk before merge

  • Merging changes upgrade behavior: configs with bindings that currently fall back to the default agent will fail validation until repaired, and doctor --fix may prune dangling bindings.
  • The PR proof demonstrates the write-guard and schema failure modes, but the exact interactive step that emptied the reporter's arrays is not isolated; this is a defense-in-depth fix around the destructive write and restart-time misroute paths.

Maintainer options:

  1. Approve the fail-closed rollout
    Land once required checks pass if maintainers accept that dangling bindings now block config load until doctor repair removes them.
  2. Keep load compatibility first
    Revise before merge to keep loading configs with dangling bindings while warning and offering doctor repair, then introduce strict validation through a later explicit compatibility step.
  3. Pause for exact onboard repro
    Pause the PR if maintainers want the exact interactive zeroing step isolated before changing schema behavior.

Next step before merge
The remaining action is maintainer compatibility approval plus normal required-check gating, not a narrow automated repair.

Security
Cleared: No concrete security or supply-chain concern was found; the diff does not change dependencies, permissions, secrets handling, or package resolution.

Review details

Best possible solution:

Land after maintainer approval of the stricter config validation/doctor-pruning behavior and required checks; if that upgrade behavior is too disruptive, preserve load compatibility and make dangling bindings a warning plus doctor repair first.

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

Do we have a high-confidence way to reproduce the issue? Yes for the core size-drop bypass and dangling-binding misroute path from current-main source plus the PR's terminal proof, but this read-only review did not rerun the exact reporter interactive setup that emptied the arrays.

Is this the best way to solve the issue?

Is this the best way to solve the issue? Yes, the patch targets both write-time prevention and restart-time validation, with doctor repair for upgrades; the remaining question is maintainer acceptance of the stricter compatibility behavior.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 329dad23f5e0.

Label changes

Label justifications:

  • P1: The linked bug can silently drop configured agents/bindings and misroute accounts after restart, affecting real channel/agent workflows.
  • merge-risk: 🚨 compatibility: The patch makes dangling binding references a config validation failure and adds doctor pruning, which can change existing upgrade behavior.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body supplies after-fix terminal/live-output proof for real fs/schema/write-guard scenarios and a real CLI rerun preserving agents and bindings.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix terminal/live-output proof for real fs/schema/write-guard scenarios and a real CLI rerun preserving agents and bindings.
Evidence reviewed

PR surface:

Source +92, Tests +341, Other 0. Total +433 across 13 files.

View PR surface stats
Area Files Added Removed Net
Source 5 100 8 +92
Tests 6 343 2 +341
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 2 2 2 0
Total 13 445 12 +433

What I checked:

  • Repository policy applied: Root AGENTS.md and scripts/AGENTS.md were read fully; the config loading, migration, setup, and fallback guidance makes this PR compatibility-sensitive rather than a purely mechanical bug fix. (AGENTS.md:25, 329dad23f5e0)
  • Current main still bypasses the size-drop guard: On current main, interactive setup and both non-interactive writers pass allowConfigSizeDrop: true for ordinary onboard writes, so current main has not already implemented the requested fix. (src/commands/onboard-non-interactive/local.ts:210, 329dad23f5e0)
  • Write guard contract: The existing config I/O guard blocks size-drop suspicious writes only when allowConfigSizeDrop is not true, so changing onboard callers materially changes the reported failure mode. (src/config/io.ts:490, 329dad23f5e0)
  • PR gates ordinary onboard writes: The PR head changes local and remote non-interactive onboard to set allowConfigSizeDrop only for opts.reset, keeping ordinary reruns under the write guard. (src/commands/onboard-non-interactive/local.ts:207, ecf265930e09)
  • PR preserves plugin-install migration shrink path: Interactive setup now defaults allowConfigSizeDrop to false but still permits the pending plugin install record migration to shrink config when the unset path matches plugins.installs. (src/wizard/setup.ts:66, ecf265930e09)
  • Binding-agent validation added: The PR head validates bindings against normalized agents.list ids and emits a path-specific schema issue for dangling binding agent ids. (src/config/zod-schema.ts:1234, ecf265930e09)

Likely related people:

  • steipete: Current-main blame for the implicated onboard write options and schema area points to Peter Steinberger, shortlog shows the largest recent contribution count across the touched paths, and the PR timeline shows steipete assigned and force-pushed maintainer commits onto this branch. (role: recent area contributor and likely follow-up owner; confidence: high; commits: e718d471f287, a374c3a5bfd5, 984ecd98ca19; files: src/wizard/setup.ts, src/commands/onboard-non-interactive/local.ts, src/commands/onboard-non-interactive/remote.ts)
  • vincentkoc: Git history shows Vincent Koc has recent commits touching the same wizard/config/schema surfaces, including config alias cleanup and wizard performance work, so he is a plausible adjacent reviewer though not the blamed owner for this specific behavior. (role: recent adjacent contributor; confidence: medium; commits: bb1b30d329, c7a947dc0a, 792653df15; files: src/wizard/setup.ts, src/config/zod-schema.ts)
  • B.K.: Commit 6878c19 changed non-interactive onboarding to preserve gateway auth during re-onboard, which is adjacent to this PR's preserve-on-rerun behavior in the same local onboarding writer. (role: adjacent onboarding behavior contributor; confidence: medium; commits: 6878c1944953; files: src/commands/onboard-non-interactive/local.ts)
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: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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. labels May 21, 2026
@clawsweeper

clawsweeper Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: 🥚 common Clockwork Review Wisp

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: 🥚 common.
Trait: purrs at green checks.
Image traits: location green-check meadow; accessory miniature diff map; palette charcoal, cyan, and signal green; mood determined; pose pointing at a small proof artifact; shell smooth pearl shell; lighting warm desk-lamp glow; background soft code-shaped tiles.
Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Clockwork Review Wisp in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 21, 2026
yetval added a commit to yetval/openclaw that referenced this pull request May 21, 2026
…penclaw#84692)

ClawSweeper review on PR openclaw#84748 flagged that
`src/commands/onboard-non-interactive/remote.ts:44` still hardcoded
`allowConfigSizeDrop: true` for ordinary `--mode remote` reruns. Same
shape as the local writer this PR already fixed — the io guard's
`size-drop:*` block is disabled and a destructive rewrite would commit
without rejection.

Apply the same reset/import-only policy to remote:

- `src/commands/onboard-non-interactive/remote.ts`: `allowConfigSizeDrop`
  is now `opts.reset === true`. Ordinary remote reruns fail closed on
  accidental shrink.
- `src/commands/onboard-non-interactive.gateway.test.ts`: new
  regression case "preserves existing agents.list and bindings on remote
  onboard rerun" — same shape as the local-mode case, asserts the seeded
  agents/bindings survive the rerun and the write uses
  `allowConfigSizeDrop: false`.
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed proof: supplied External PR includes structured after-fix real behavior proof. labels May 21, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 21, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 21, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 21, 2026
yetval added a commit to yetval/openclaw that referenced this pull request May 27, 2026
…penclaw#84692)

ClawSweeper review on PR openclaw#84748 flagged that
`src/commands/onboard-non-interactive/remote.ts:44` still hardcoded
`allowConfigSizeDrop: true` for ordinary `--mode remote` reruns. Same
shape as the local writer this PR already fixed — the io guard's
`size-drop:*` block is disabled and a destructive rewrite would commit
without rejection.

Apply the same reset/import-only policy to remote:

- `src/commands/onboard-non-interactive/remote.ts`: `allowConfigSizeDrop`
  is now `opts.reset === true`. Ordinary remote reruns fail closed on
  accidental shrink.
- `src/commands/onboard-non-interactive.gateway.test.ts`: new
  regression case "preserves existing agents.list and bindings on remote
  onboard rerun" — same shape as the local-mode case, asserts the seeded
  agents/bindings survive the rerun and the write uses
  `allowConfigSizeDrop: false`.
@yetval
yetval force-pushed the fix/onboard-preserve-agents-bindings-84692 branch from 8b6184b to 8923d78 Compare May 27, 2026 05:29
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
@yetval

yetval commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Rebased onto current openclaw/main (44c1cc8285c). 3-commit branch replayed cleanly. No functional change. New head: 8923d785e2f.

@clawsweeper

clawsweeper Bot commented May 27, 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.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
@steipete steipete self-assigned this May 27, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
yetval and others added 5 commits May 27, 2026 10:05
)

Default ordinary `openclaw onboard` reruns were silently writing the
config with `allowConfigSizeDrop: true`, so the generic config-write
size-drop guard never fired on a destructive rewrite. Combined with a
schema that did not validate `bindings[].agentId` against
`agents.list`, a stripped config could be committed, then load cleanly
at the next gateway restart while every bound account misrouted.

Gate the size-drop bypass behind explicit reset/import flows:

- `src/wizard/setup.ts`: `writeWizardConfigFile(config, opts?)` now
  takes an `allowConfigSizeDrop` flag that defaults to `false`. A new
  `configResetPerformed` boolean is set only when the user picks the
  reset action, and is threaded into every write call. Migration import
  passes `true` explicitly because that flow legitimately shrinks the
  config.
- `src/commands/onboard-non-interactive/local.ts`: `allowConfigSizeDrop`
  is now `opts.reset === true` instead of hardcoded `true`.
- `src/config/zod-schema.ts`: the top-level `superRefine` now rejects
  any `bindings[].agentId` that is missing from `agents.list`, mirroring
  the existing broadcast check. Skipped when `agents.list` is empty so
  legacy configs without a populated agents list still load.

Tests:

- `src/commands/onboard-config.test.ts`: rerun preserves `agents.list`
  + `bindings`.
- `src/commands/onboard-non-interactive.gateway.test.ts`: non-interactive
  rerun preserves seeded agents/bindings and writes with
  `allowConfigSizeDrop: false`.
- `src/config/config.schema-regressions.test.ts`: dangling agentId
  rejected; valid binding accepted; empty agents.list passthrough.
- `src/wizard/setup.test.ts`: updated existing write-options assertion
  to expect the new safe default.
…claw#84692 lint

Resolves the two `pnpm lint` errors flagged by ci `check-lint`:

- `typescript(no-unnecessary-type-arguments)`: `readTestConfig<OpenClawConfig>()`
  is the default type parameter, so omit the explicit `<OpenClawConfig>`.
- `typescript(prefer-includes)`: replace
  `/Unknown agent id "ghost"/.test(message)` with
  `message.includes('Unknown agent id "ghost"')`.

Behavior identical; only lint shape changes.
…penclaw#84692)

ClawSweeper review on PR openclaw#84748 flagged that
`src/commands/onboard-non-interactive/remote.ts:44` still hardcoded
`allowConfigSizeDrop: true` for ordinary `--mode remote` reruns. Same
shape as the local writer this PR already fixed — the io guard's
`size-drop:*` block is disabled and a destructive rewrite would commit
without rejection.

Apply the same reset/import-only policy to remote:

- `src/commands/onboard-non-interactive/remote.ts`: `allowConfigSizeDrop`
  is now `opts.reset === true`. Ordinary remote reruns fail closed on
  accidental shrink.
- `src/commands/onboard-non-interactive.gateway.test.ts`: new
  regression case "preserves existing agents.list and bindings on remote
  onboard rerun" — same shape as the local-mode case, asserts the seeded
  agents/bindings survive the rerun and the write uses
  `allowConfigSizeDrop: false`.
@steipete
steipete force-pushed the fix/onboard-preserve-agents-bindings-84692 branch from d092469 to c825284 Compare May 27, 2026 09:09
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label May 27, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 27, 2026
steipete pushed a commit to yetval/openclaw that referenced this pull request May 27, 2026
…penclaw#84692)

ClawSweeper review on PR openclaw#84748 flagged that
`src/commands/onboard-non-interactive/remote.ts:44` still hardcoded
`allowConfigSizeDrop: true` for ordinary `--mode remote` reruns. Same
shape as the local writer this PR already fixed — the io guard's
`size-drop:*` block is disabled and a destructive rewrite would commit
without rejection.

Apply the same reset/import-only policy to remote:

- `src/commands/onboard-non-interactive/remote.ts`: `allowConfigSizeDrop`
  is now `opts.reset === true`. Ordinary remote reruns fail closed on
  accidental shrink.
- `src/commands/onboard-non-interactive.gateway.test.ts`: new
  regression case "preserves existing agents.list and bindings on remote
  onboard rerun" — same shape as the local-mode case, asserts the seeded
  agents/bindings survive the rerun and the write uses
  `allowConfigSizeDrop: false`.
@steipete
steipete force-pushed the fix/onboard-preserve-agents-bindings-84692 branch from 0bad5a1 to 0790dd5 Compare May 27, 2026 09:26
@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label May 27, 2026
@steipete
steipete force-pushed the fix/onboard-preserve-agents-bindings-84692 branch from 0790dd5 to ecf2659 Compare May 27, 2026 09:33
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label May 27, 2026
@steipete

Copy link
Copy Markdown
Contributor

Verification for head ecf265930e09477d4a7c660791a56c6d660e118b:

Behavior addressed: ordinary openclaw onboard reruns preserve agents.list and bindings; size-drop bypass remains scoped to reset/import/plugin-install migration paths; doctor repair prunes dangling bindings only when agents.list is valid enough to do so safely.
Real environment tested: GitHub Actions on the PR head, plus local macOS focused smoke/lint.
Exact steps or command run after this patch:

  • pnpm exec oxfmt --write --threads=1 src/commands/doctor/shared/legacy-config-core-migrate.ts src/commands/doctor/shared/legacy-config-migrate.test.ts scripts/lib/openclaw-e2e-instance.sh
  • node scripts/run-oxlint-shards.mjs --only core --only scripts --threads=8
  • manual shell smoke for openclaw_e2e_install_package with timeout unavailable and Node watchdog active
  • pnpm exec tsx -e 'import { normalizeCompatibilityConfigValues } from "./src/commands/doctor/shared/legacy-config-core-migrate.ts"; ...' smoke for valid vs malformed agents.list
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
    Evidence after fix:
  • CI run 26503163785: success
  • Real behavior proof run 26503171044: success
  • CodeQL Critical Quality run 26503163782: success
  • OpenGrep PR Diff run 26503163828: success
  • Autoreview: clean, no accepted/actionable findings
    Observed result after fix: PR status is clean/mergeable with no pending or failed required checks.
    What was not tested: local Vitest in this checkout hung silently even with timeout; the equivalent PR CI suite completed successfully on GitHub Actions.

@steipete
steipete merged commit bbdff39 into openclaw:main May 27, 2026
105 of 106 checks passed
steipete added a commit that referenced this pull request May 27, 2026
Fix non-interactive and wizard onboarding reruns so existing agent lists and bindings are preserved unless the user explicitly resets config.

Isolate legacy `plugins.installs` migration into its own write so the config size-drop allowance cannot mask unrelated config loss, while preserving new or repaired install records for the final plugin-index commit. Also keep shrinkwrap generation pinned to pnpm-locked transitive patch versions only when the dependency edge still allows that version, and isolate the tooling Vitest shard that mutates process state.

Fixes #84692.
Replaces #84748.

Co-authored-by: yetval <[email protected]>
@steipete

Copy link
Copy Markdown
Contributor

Thanks @yetval. This is now landed via replacement PR #87328 in commit de5971e.

I could not update this fork branch directly because maintainer edits were disabled / the branch was not writable from the maintainer remote, so I recreated the fix on a maintainer branch, preserved your co-author credit in the squash commit, and merged that replacement.

For future PRs, enabling "Allow edits by maintainers" lets us push small fixups directly to the contributor branch.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 28, 2026
Fix non-interactive and wizard onboarding reruns so existing agent lists and bindings are preserved unless the user explicitly resets config.

Isolate legacy `plugins.installs` migration into its own write so the config size-drop allowance cannot mask unrelated config loss, while preserving new or repaired install records for the final plugin-index commit. Also keep shrinkwrap generation pinned to pnpm-locked transitive patch versions only when the dependency edge still allows that version, and isolate the tooling Vitest shard that mutates process state.

Fixes openclaw#84692.
Replaces openclaw#84748.

Co-authored-by: yetval <[email protected]>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
Fix non-interactive and wizard onboarding reruns so existing agent lists and bindings are preserved unless the user explicitly resets config.

Isolate legacy `plugins.installs` migration into its own write so the config size-drop allowance cannot mask unrelated config loss, while preserving new or repaired install records for the final plugin-index commit. Also keep shrinkwrap generation pinned to pnpm-locked transitive patch versions only when the dependency edge still allows that version, and isolate the tooling Vitest shard that mutates process state.

Fixes openclaw#84692.
Replaces openclaw#84748.

Co-authored-by: yetval <[email protected]>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Fix non-interactive and wizard onboarding reruns so existing agent lists and bindings are preserved unless the user explicitly resets config.

Isolate legacy `plugins.installs` migration into its own write so the config size-drop allowance cannot mask unrelated config loss, while preserving new or repaired install records for the final plugin-index commit. Also keep shrinkwrap generation pinned to pnpm-locked transitive patch versions only when the dependency edge still allows that version, and isolate the tooling Vitest shard that mutates process state.

Fixes openclaw#84692.
Replaces openclaw#84748.

Co-authored-by: yetval <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openclaw onboard re-run discards existing agents.list and bindings — silent loss of all configured agents

2 participants