Skip to content

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

Description

@mrbeandev

Summary

Reloading the browser tab on Step 3 makes the frontend re-send a force-restart to the backend. The backend kills the running subprocess and deletes the run's data files, then starts again from round 0. Everything the timeline / posts / comments views render is gone, permanently. There is no confirmation prompt and nothing indicates that reloading is destructive.

Reproduction

  1. Start a simulation and let it run a few rounds until the timeline populates (e.g. TOTAL EVENTS: 209).
  2. Refresh the tab, or close and reopen it.
  3. The round counter resets to 0/35, ACTS 0, the timeline shows WAITING FOR AGENT ACTIONS..., and the previous run's posts and comments are gone.

Root cause

frontend/src/components/Step3Simulation.vue:691-696 starts a simulation on every mount, with no check for an existing run:

onMounted(() => {
  addLog(t('log.step3Init'))
  if (props.simulationId) {
    doStartSimulation()
  }
})

The simulation id is a route path param (frontend/src/router/index.js:28/simulation/:simulationId/start, read at frontend/src/views/SimulationRunView.vue:94), so it survives a reload and the mount call fires again with a valid id.

doStartSimulation() (Step3Simulation.vue:383) hardcodes force: true on every call (:401).

On the backend, POST /simulation/start treats that as permission to tear the run down (backend/app/api/simulation.py:1626-1667). stop_simulation() SIGTERMs then SIGKILLs the process group (backend/app/services/simulation_runner.py:1004), and cleanup_simulation_logs() (simulation_runner.py:1398-1431) deletes:

  • run_state.json
  • twitter/actions.jsonl, reddit/actions.jsonl
  • twitter_simulation.db, reddit_simulation.db
  • simulation.log, stdout.log, stderr.log, env_status.json

Those files are the only copy. get_all_actions() reads actions.jsonl off disk on every call (simulation_runner.py:1156-1198) and the /posts and /comments endpoints open the SQLite files directly (simulation.py:2169, :2247). This is not a display or caching problem — the underlying data is gone. Zep graph data survives only because it lives outside the deleted directory, not because anything preserves it.

Two details worth flagging:

  • The cleanup call at simulation.py:1656 sits outside the if needs_finalization: block, so a completed or stopped run is wiped too. Reopening Step 3 to review a finished run destroys it.
  • _check_simulation_prepared() returns True for a run in progress (profiles and config are still on disk), so the destructive branch is reachable rather than gated off.

Affected versions

All of them. The onMounted auto-start, force: true, the /simulation/:simulationId/start route, the backend force → cleanup wiring, and cleanup_simulation_logs() deleting the action logs all arrived in the same commit, f8a5881 (2025-12-12) — the commit that added Step3Simulation.vue.

Checked force:true + onMounted auto-start at f8a5881, 0577ecd, d768fd1, c91dad3, 2fd1227, 3a8451c, 867b54c, e4aa38d, b5b53ac — present at every one. d768fd1 later added the two *_simulation.db files to the delete list, widening the blast radius from the timeline to posts and comments. There is no tag or release to fall back to.

Impact

Simulations run for hours — the UI logs its own 60 minutes per round time config. Any accidental refresh, tab restore, browser crash recovery, or close-and-reopen discards the entire run and the LLM spend behind it, with no warning and no recovery path.

Related issues and PRs

Neither PR is merged, and the bug has no issue of its own — which is why I am filing this one, so the data loss can be tracked and referenced independently of either PR's review.

Suggested fix

Probe GET /simulation/<id>/run-status before deciding what to do on mount. The endpoint already exists (simulation.py:1865) and the frontend wrapper is already there (frontend/src/api/simulation.js:102):

  • run already exists (live or terminal) → adopt it, resume polling if live, load the existing timeline if not
  • genuinely nothing there → start as today

force: true should only ever be sent for an explicit, user-initiated restart, never as the implicit default on component mount.

Environment

  • Commit reviewed: b5b53acc57189a4a42e44a23e149dc655c98fe82 (2026-08-03)

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions