Skip to content

fix(simulation): stop discarding an existing run when Step 3 remounts - #758

Open
mrbeandev wants to merge 1 commit into
666ghj:mainfrom
mrbeandev:fix/preserve-simulation-run-on-remount
Open

fix(simulation): stop discarding an existing run when Step 3 remounts#758
mrbeandev wants to merge 1 commit into
666ghj:mainfrom
mrbeandev:fix/preserve-simulation-run-on-remount

Conversation

@mrbeandev

Copy link
Copy Markdown

Fixes #757.

Problem

Step3Simulation.vue starts a simulation on every mount with force: true. The simulation id is a route path param, so a browser refresh remounts the component and re-sends that request — the backend kills the subprocess and deletes run_state.json, both actions.jsonl action logs and both *_simulation.db files. Those files are the only copy of the timeline, posts and comments, so a refresh permanently discards the run. Full analysis in the issue.

Change

onMounted now calls resumeOrStartSimulation(), which reads GET /simulation/<id>/run-status before deciding:

Backend state Behaviour
idle and no recorded actions genuinely fresh — starts exactly as before
starting / running / paused / stopping adopts the run, seeds the round counters, resumes both polls — no restart
stopped / completed / failed loads the existing timeline once and sets phase = 2 so Generate Report is reachable — no restart
status probe fails does nothing and surfaces the error, rather than starting and risking a run it could not verify

force is no longer hardcoded to true. The one remaining call site only runs when there is nothing to lose, and the flag should never be an implicit default.

Three log strings added to locales/en.json and locales/zh.json.

Why this is a separate PR

The fix already exists inside two open PRs, and I would rather say so up front than look like I am duplicating work:

Both are worth merging on their own merits and this is not meant to pre-empt either. But the data-loss fix itself is ~60 lines, and while it waits behind a larger review every refreshed tab costs someone a multi-hour run. This PR does only that one thing so it can be reviewed and merged on its own schedule. If #718 or #693 lands first, close this one — no complaints.

The code here was written from the issue analysis rather than taken from either branch, so there is nothing to untangle if you merge one of them instead.

Trade-off, stated plainly

Re-entering Step 3 for a simulation that already ran now adopts it instead of restarting it. Step 2's Next button reuses the same simulation id (SimulationView.vue:164-167), so "go back to Step 2, then forward again" no longer re-runs the simulation.

That is deliberate — silently deleting a finished run is worse than declining to restart it — and it is not a new gap. The only explicit restart control, <button class="retry-btn" @click="doStartSimulation">, was removed in d768fd1 (2025-12-12), which is how force: true ended up reachable only from mount in the first place. Related leftover: startError is still assigned but never rendered anywhere, because it belonged to that same removed retry UI.

If you would like an explicit "Restart run" control (with a confirmation) restored as part of this, say the word and I will add it. I left it out to avoid folding a UI decision into a bug-fix PR.

Validation

  • npm run build passes; the remaining warnings are the pre-existing chunk-size and dynamic-import notices on main
  • en.json and zh.json parse, and key parity checked in both directions
  • every field used (runner_status, current_round, twitter_current_round, reddit_current_round, total_actions_count) verified against SimulationRunState.to_dict()
  • diff is 3 files, +66/−2, with no package-lock.json churn

Step3Simulation mounted with a simulation id and immediately called
doStartSimulation() with force: true. Because the id lives in the route
path (/simulation/:simulationId/start), a browser refresh remounted the
component and re-sent that request, so the backend stopped the running
subprocess and cleaned the run directory: run_state.json, the twitter and
reddit actions.jsonl action logs, and both *_simulation.db files. The
timeline, posts and comments views read those files directly, so the data
was unrecoverable.

Probe the run status before deciding what to do. A simulation that has
never run (runner_status "idle" with no recorded actions) still starts
exactly as before. Anything else is adopted instead of restarted: live
runs resume polling, finished runs load their existing timeline read-only.
If the status probe itself fails we now do nothing rather than risk
overwriting a run we could not verify.

force is also no longer hardcoded to true. The remaining call site only
runs when there is nothing to lose, and the flag should never be an
implicit default.
CyrusAuyeung added a commit to CyrusAuyeung/MiroFish that referenced this pull request Aug 6, 2026
@CyrusAuyeung

CyrusAuyeung commented Aug 6, 2026

Copy link
Copy Markdown

Thanks for isolating this data-loss path. To keep both branches independently mergeable, I removed only the overlapping Step 3 remount/existing-run recovery hunk from #718 in bd4babb.

#718 remains intended for merge for its independent provider, IPC, state, initial-post, and action-integrity fixes. Its remaining Step3Simulation.vue changes only cover action filtering, counts, and display details, so the two PRs no longer create a winner-takes-all conflict over the remount/force logic.

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.

Refreshing the Step 3 page force-restarts the simulation and permanently deletes the run's data

2 participants