Skip to content

fix(migration): refuse to overwrite diverged target active state - #8683

Open
Duang777 wants to merge 1 commit into
loopx-project:mainfrom
Duang777:codex/migrate-state-keep-diverged-active-state-20261113
Open

Duang777 wants to merge 1 commit into
loopx-project:mainfrom
Duang777:codex/migrate-state-keep-diverged-active-state-20261113

Conversation

@Duang777

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

  • Outcome basis / optional anchor: self-found core defect. No public issue; the request in this PR is the basis.
  • Goal/source and gap: copy_active_state_files in loopx/state_migration.py published the rewritten legacy active-state file over the target path unconditionally, while the runtime copy next to it skips existing files.
  • Observable before → after, with the validation row that proves it: After a first migration the target was edited; a second migrate_legacy_state(..., execute=True) reported copied: True and rolled the target back to the stale legacy copy, with no backup or preview warning. After: preview and execute both raise ValueError: migrated active state already exists with different content, and the target file and registry bytes are unchanged. An identical rerun and an in-place rewrite of the same file still work. Every target is checked before the first copy, so a conflict leaves no partial write. Rows: regression_parity, unit.
  • Issue/task and intended base: none. Base is main at 7bb1b01.

Author Declaration

  • Written by: agent, Claude Sonnet 5.5, for @Duang777

Implemented against

  • Specification and revision: no written specification; the request in this PR is the basis.
  • Criteria:
Criterion (spec clause) Disposition Symbol / path Test or command
A rerun must not overwrite live state with a stale copy implemented copy_active_state_files in loopx/state_migration.py tests/test_state_migration.py::test_repeated_migration_does_not_overwrite_diverged_target_active_state
  • Self-check before submission: reproduced the defect on the unfixed code, then confirmed the new test fails before and passes after. Did not run the full suite or CI-equivalent gates locally. Noticed but did not change: --goal-id-map does not validate new ids as path segments.

Scope And Continuation

  • Completed scope and remaining work: complete within this scope. There is no --force path to overwrite a diverged target deliberately.
  • Slice boundary / successor: N/A, one independent defect.

Validation

  • Tested revision: 0199f6fcdc8fa43939b2d94202ef4a0b22eddf62 for the diff; local runs used a working tree whose touched files were blob-identical to main 7bb1b01 before the change.
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
regression_parity passed The new test fails on the unfixed code (DID NOT RAISE) and passes with the fix.
unit passed tests/test_state_migration.py plus the botmux file, 25 passed; ruff check clean.
unit failed test_state_migration_authority_shadow.py and test_shadow_writer_boundaries.py had 5 failures with and without this change on the stale local tree; not re-run on main.
  • Coverage and gaps: The 5 local failures need confirming against CI on this head.

Frontend / Visual Evidence

  • UI impact: none

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: Core control-plane hardening.

Shared-authority RFC fixture impact

N/A

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • Existing English/Chinese RFC pairs (including ledger entries) both have a diff, and the local premerge check passes, or no paired RFC changed. No RFC changed.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

🤖 Generated with Claude Code

`migrate-state --copy-active-state` published the rewritten legacy
active-state file over the target path unconditionally, while the runtime
copy next to it skips existing files. Re-running a migration (for example
`--all-goals` after more legacy Goals appeared, or any retry after the target
Goal had been worked) silently replaced the live target Todos with the stale
legacy copy: completed Todos reopened and Todos added since the first copy
were lost, with no backup and no warning in the preview.

Check every target before the first copy and fail closed when an existing
target differs from the bytes the migration would publish. An identical
rerun and an in-place rewrite of the same file stay idempotent, and the
conflict is raised in preview as well as execute so nothing is partially
written.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: duang777 <duangjl007@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant