Skip to content

fix(core): move sessions to the remote-derived project id when a remote is added later - #58668

Closed
holny wants to merge 1 commit into
anomalyco:devfrom
holny:fix-57985-project-id-stable
Closed

holny wants to merge 1 commit into
anomalyco:devfrom
holny:fix-57985-project-id-stable

Conversation

@holny

@holny holny commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #57985

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When a repo gains a git remote, its project id changes from the root-commit-derived one to a remote-derived one. The first resolution after git remote add upserts a second project row for the same directory, and every session created under the old id stops showing up in the project's session list (nothing is deleted, but the sessions are stranded).

The session projector already re-points sessions when a directory adopts a different identity (Worktree.Event.Resolved with adopted), but adoption only covered markerless projects being absorbed by a repo. This PR adds git projects that previously claimed the same directory row to the adoption candidates, so sessions created before the remote existed follow the directory to its new id. The global project stays excluded — its unborn-git directory rows are already handled through the previous/global candidates.

Stranded sessions from before this fix migrate on the next resolution of the directory (for example when the background service re-resolves it).

How did you verify your code works?

  • New regression test in packages/core/test/session-create.test.ts: create a session in a repo without a remote, add a remote, resolve — the session moves to the remote-derived project id and shows up in that project's session list. Fails without the fix (session stays under the root-commit id).
  • packages/core: test/session-create, test/project, test/session-projector, test/session-move all green; full core suite run with only the pre-existing environment failures (ShellTool/Git trees/Worktree).

Checklist

  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if applicable)

Added by the opencode bot (AI-generated).

Related: #44256

…te is added later

A directory's project id becomes remote-derived the first time a remote
exists, which minted a second project row and stranded every session
created under the earlier id. Include the git project that previously
claimed the directory in the worktree adoption candidates so the
existing session projector re-points those sessions, same as the
markerless adoption path.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes #57985 as described, but it moves too much in two other cases.

I ran the issue's reproduction from source. On the base, the session disappears from session list after git remote add. With this change it is still listed. The core session and project tests pass, and so does tsc.

The public API is unchanged: Worktree.Event.Resolved already has an adopted field, and this change only adds more IDs to it.

The problem is that adopted IDs are treated by the session projector as whole-project adoptions. It moves every session of that project, with no directory filter, and recomputes each session's subpath from the old project's main folder. That suited markerless projects, but not git projects found through a matching worktree row. I checked both effects with extra tests on the base and with this change:

  • Lost subfolder: a session created in <repo>/pkg moves to the new project, but its subpath changes from pkg to empty.
  • Sessions from another clone: clones a and b share a remote-derived project. If a changes its remote URL, b's session also moves to a's new project, even though b still resolves to the old ID.

One way to fix both: send the old ID as previous instead of in adopted, or add a separate field for it. The projector then filters by session directory and keeps each session's own path.

Permission rules keyed by the old project ID are still not moved, as the issue's analysis pointed out.

Related: #44256

)
.all()
.pipe(Effect.orDie)
const adoptedIDs = [...new Set([...adopted.map((candidate) => candidate.id), ...adopting.map((row) => row.id)])]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting these IDs into adopted makes the session projector move every session of the old project. It skips the directory filter and recomputes path from the old project's main folder rather than the session's own folder. I checked two results:

  • A session created in <repo>/pkg ends up with an empty subpath. On the base it keeps pkg.
  • With two clones a and b sharing a remote-derived ID, changing a's remote also moves b's session to a's new project, while b still resolves to the old ID.

An old git identity for the same directory works more like previous. The projector would then select only sessions under item.directory and keep each session's own path. You could pass it there, or in a new field that the projector handles with the directory filter.

@holny

holny commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Freeing the slot for now — I'm up against the open-PR limit and #59794 (which I claimed yesterday) needs it more. Argszero's #59946 already covers the session-list side of this ID-switch family, so the marginal value of keeping this open is lower right now. The branch stays put; if the remote-derived-ID move itself still needs a fix after #59946 lands, I can reopen.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

project: adding a git remote changes the project ID and hides every earlier session

1 participant