Skip to content

Project identity should anchor on directory path, not git detection — fixes global-bucket fallback and session isolation #47885

Closed as not planned

Description

@Trickster-ID

Summary

ProjectV2.resolve() in packages/core/src/project.ts makes project identity fully dependent on git detection succeeding. When git.repo.discover() returns null — non-git directory, git not yet initialized, transient discovery failure, workspace root above multiple repos — the fallback collapses both the project ID and the directory into shared, non-unique values:

const repo = yield* git.repo.discover(input)
if (!repo) return { id: ID.global, directory: AbsolutePath.make(path.parse(input).root), vcs: undefined }

Two separate directories that both fail git detection get:

  • same id: ID.global
  • same directory: "/" (root of the filesystem, via path.parse(input).root)

This means any two unrelated directories that fail git discovery become indistinguishable as the same project, merging their sessions, permissions, and history in the UI.

Why this happens more than it seems

Failing git discovery isn't rare/edge-case:

  • Workspace layouts where the launch directory is a plain folder containing multiple git repos as subdirectories (common multi-repo workspace setup).
  • Directory not yet git init'd when opencode starts.
  • Any transient failure in git.repo.discover().

In all these cases the directory path itself is still perfectly known and unique — we just choose to throw it away and substitute /.

Downstream impact (confirmed via source + related issues)

  1. Session bucket collision — /sessions picker filters by projectID. All directories that fall back to global share one bucket, so sessions from completely unrelated projects show up mixed together. (New session created via /new gets projectID=global instead of the directory's project, making prior sessions invisible in /sessions #42230, Same git directory gets two project IDs and /sessions only shows the latest project's sessions #36464, bug(core): empty git repos share the same "global" project ID, causing sessions to leak across unrelated directories #39773, session list and TUI mix sessions from unrelated non-git directories #38529)
  2. Permission path breakage — tool permission rules evaluate paths relative to worktree. With worktree === "/", path.relative("/", "/home/.../file.md") produces a long root-relative fragment instead of a project-relative path, so scoped permission rules silently stop matching. (Non-git projects use "/" as worktree, breaking permission path resolution #24694)
  3. Security concern raised by community — root-relative worktree implicitly gives filesystem-root-scoped permission context to a directory that never asked for it. See community pushback in Non-git projects use "/" as worktree, breaking permission path resolution #24694.

This was already root-caused and fixed once: #24694 had a working PR (#33668, fix(project): use directory root for non-git projects) that normalized the opened directory as worktree/sandbox instead of /. Both the issue and the PR were closed automatically by the 60-day stale-bot, not by a maintainer decision, and multiple users have asked for reopening since.

Proposed fix direction

Stop treating git discovery as a prerequisite for project identity. Instead:

  • Primary identity key = normalized absolute directory path. Every directory gets a stable, unique project identity by default, git or not.
  • Git remains an enrichment/merge signal, not a gate. If git is present, use remote URL / root commit to additionally unify multiple worktrees or clones of the same repo under one project — exactly like today's remote(repo) ?? previous ?? root(repo) logic — but only to merge, never to collapse unrelated directories into global.
  • ID.global should be reserved for the literal filesystem root or an explicit "no project" case, not used as a catch-all for "git detection failed here."
  • Directory used for worktree/sandbox/permission scoping should always be the resolved input directory, never path.parse(input).root.

This restores directory-level isolation (which several users report as previous/expected behavior) without requiring a new config flag — every directory is inherently its own project by default, and git just adds cross-directory linking on top when applicable.

Related issues

Environment

  • opencode version: 1.18.29
  • OS: macOS (darwin)
  • Reproduced with: workspace root directory containing multiple independent git repos as subdirectories, launched opencode from the workspace root instead of from inside each repo

Activity

  1. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  2. akib1689 commented on Oct 12, 2026

    @akib1689

    This is still the problem is still present in the V2. reproduced in OpenCode 2.0.27 (macOS, Homebrew)

    #38529, #53450, #54679 also mention similar sort of things..

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions