[Bug]: Dev database seed keeps settle scripts, so the dev server runs them in the developer's real worktrees#33907

opened8h agobyLLenoridev

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. In a source database, have a project with a script that sets runOnSettle: true, and a stopped, unsettled thread in that project whose worktreePath is an existing checkout. The thread has been idle for more than 7 days.
  2. Seed a dev database from it: the first vp run dev in a worktree, or vp run migrate-dev-db.
  3. Start the dev server. Do nothing else.

Fixture repro: I ran migrate-dev-db.ts on a fixture database whose paths were all temporary directories, then booted bin.ts serve on the result with default settings and no settings.json. The settle script was pwd > "$PWD/settle-script-ran.marker".

Expected behavior

AGENTS.md says the seed exists "so your dev server never runs the developer's agents". A copied database should not run the developer's project scripts in the developer's real checkouts on its own. Threads and scripts should still appear in the dev UI.

Actual behavior

The first automatic settlement sweep settles the seeded thread, and the settle script runs in the worktreePath copied from the source database:

  • migrate-dev-db keeps each project's scripts_json, including runOnSettle, and each thread's real worktreePath.
  • With no settings.json, the server folds scripts_json into the dev home's project settings on first load.
  • Automatic settling is on by default (sidebarAutoSettleAfterDays is 7 and sidebarAutoSettleOnMerge is true).
  • thread.auto-settle dispatches thread.settle, which emits thread.settled and sets settledOverride to "settled".
  • cleanUpSettledThread checks that the worktree exists, then calls projectScripts.runForThread({ trigger: "settle", worktreePath, ... }). Nothing checks which install the thread came from.

Settling a seeded thread by hand reaches the same handler, because thread.settle emits the same event. I did not run that path in the UI.

Impact

Major degradation or frequent failure

Version or commit

main @ 41443e5706

Environment

Linux, Node 24.22.0, server started from a git worktree

Logs or stack traces

shell
1== seeded project scripts
2[{"id":"teardown","name":"Teardown","command":"pwd > \"$PWD/settle-script-ran.marker\"","icon":"configure","runOnWorktreeCreate":false,"runOnSettle":true}]
3== seeded thread before boot
4settledOverride=null hasWorktreePath=1
5== boot real server, default settings, no settings.json
6thread settled by the automatic sweep: yes
7settledOverride="settled"
8settle script RAN in worktree: $TMP/e2e-before/real-worktree

Workaround

Run dev with --no-seed, or remove runOnSettle from the seeded project's scripts before the first dev server start.

Same class as #15513, a copied database acting on the original install's worktrees. #32736 reported the pull request watch path of that class, and #32744 fixed it. This is a separate path that #32744 does not touch.

Reported with Claude Code (Claude Opus 5.5).