feat(core): project copying and tracking paths#30139
Draft
jlongster wants to merge 2 commits into
Draft
Conversation
e1c3dc0 to
49f3cee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Project Paths And Copies
Reference for the implemented local project-path and project-copy system: responsibilities, dependencies, API shapes, errors, and call flows.
Overview
Core Domain
Project
Source:
packages/core/src/project.tsResponsibility: resolve project identity and read known local filesystem roots for a project.
Dependencies
GitProject.resolve(...)to inspect the repository, normalized remote, and root commits.ProjectPathTableProject.paths(...)as the persisted read model.Databaseproject_pathquery.Methods
resolveAbsolutePathpaths{ projectID: Project.ID }Array<{ path: AbsolutePath; primary: boolean }>commit{ store: AbsolutePath; id: Project.ID }voidProject.resolveCalls
Git.find(directory).For a non-Git location, returns:
For a Git checkout, calls
Git.remote(repo)andGit.roots(repo)to derive stable identity.Return value for a Git checkout:
Error behavior: Git inspection failure degrades to missing identity inputs rather than producing a declared failure channel.
Project.pathsproject_pathmatchingprojectID.AbsolutePathat the service boundary.ProjectCopy, Git discovery, pruning, or copy creation.ProjectPathTable
Source:
packages/core/src/project/path.sql.tsResponsibility: store known local materializations of projects.
Stored Shape
Constraints
ProjectTable(project_id, path)project_idwhereprimary = trueWriters And Readers
Project.pathsProjectCopyPersisted Project Opening Flow
Source:
packages/opencode/src/project/project.tsResponsibility: open a directory in the application and persist legacy project state plus its active local path.
Call Flow
rememberProjectPathProject.ID.global.Project ID Change Behavior
project_pathrows are not migrated when the project ID associated with a checkout changes.Copy Management
ProjectCopy
Source:
packages/core/src/project/copy.tsResponsibility: manage and explicitly discover physical local copies while keeping
Project.paths(...)read-only.Dependencies
ProjectPathTableAppFileSystemEventV2project.paths.updatedonly after persisted paths change.Service API
strategies{ projectID }Array<{ id: "git_worktree"; name: string }>create{ projectID, strategy: "git_worktree", path }{ path: AbsolutePath }remove{ projectID, strategy: "git_worktree", path }voidrefresh{ projectID, strategy?: "git_worktree" }voidDomain Errors
Create Flow
Return value:
Remove Flow
Return value:
void.Refresh Flow
Return value:
void.Important behavior:
Project.pathsnever initiates it.ProjectCopy.Event.Updated
Source:
packages/core/src/project/copy.tsResponsibility: notify consumers that
Project.paths(...)can now return a changed set.ProjectCopy.Strategy
Source:
packages/core/src/project/copy.tsResponsibility: define one physical copy mechanism without owning persistence or transport.
ProjectCopy.ProjectPathTable.primary.Initial Strategy
GitWorktree
Source:
packages/core/src/project/copy-strategies.tsResponsibility: create, remove, and enumerate linked Git worktrees as physical project copies.
Dependencies
Git.ServiceProjectCopyMethods
create({ primaryPath, path })Calls
Git.worktreeCreate({ repo, path }).Canonicalizes the created destination.
Returns:
remove({ primaryPath, path })Git.worktreeList(repo)to confirm the target is linked.Git.WorktreeErrorif the target is not in that list.Git.worktreeRemove({ repo, path })when confirmed.void.list({ primaryPath })Calls
Git.worktreeList(repo).Filters out the primary source path.
Canonicalizes each remaining path.
Returns:
Errors
Git.WorktreeErrorProjectCopy.PathUnavailableErrorGit Operations
Git
Source:
packages/core/src/git.tsResponsibility: perform Git repository inspection and execute typed worktree commands.
Project Identity Methods
find(path)Project.resolveGit.Repo | undefinedremote(repo, name?)Project.resolvestring | undefinedroots(repo)Project.resolvestring[]Worktree Methods
worktreeCreate({ repo, path })GitWorktree.creategit worktree add --detach <path> HEADvoidGit.WorktreeErrorworktreeRemove({ repo, path })GitWorktree.removegit worktree remove --force <path>voidGit.WorktreeErrorworktreeList(repo)GitWorktree.list/removegit worktree list --porcelainAbsolutePath[]Git.WorktreeErrorError Shape
HTTP API
Project Paths Endpoint
Sources:
packages/opencode/src/server/routes/instance/httpapi/groups/project.tspackages/opencode/src/server/routes/instance/httpapi/handlers/project.tsGET /project/:projectID/pathsProject.paths({ projectID })200 Array<{ path: string; primary: boolean }>Properties:
ProjectCopy.refresh.ProjectCopy Endpoints
Sources:
packages/opencode/src/server/routes/instance/httpapi/groups/project-copy.tspackages/opencode/src/server/routes/instance/httpapi/handlers/project-copy.tsGET /project/:projectID/copy/strategyProjectCopy.strategies200 Array<{ id: "git_worktree"; name: string }>POST /project/:projectID/copy{ strategy: "git_worktree"; path: string }ProjectCopy.create200 { path: string }400DELETE /project/:projectID/copy{ strategy: "git_worktree"; path: string }ProjectCopy.remove204 No Content400POST /project/:projectID/copy/refresh{ strategy?: "git_worktree" }ProjectCopy.refresh204 No Content400JavaScript SDK
Generated source:
packages/sdk/js/src/v2/gensdk.project.paths({ projectID })GET /project/{projectID}/pathsArray<{ path: string; primary: boolean }>sdk.projectCopy.strategies({ projectID })GET /project/{projectID}/copy/strategyArray<{ id: "git_worktree"; name: string }>sdk.projectCopy.create({ projectID, strategy, path })POST /project/{projectID}/copy{ path: string }400for rejected domain operationsdk.projectCopy.remove({ projectID, strategy, path })DELETE /project/{projectID}/copy400for rejected domain operationsdk.projectCopy.refresh({ projectID, strategy? })POST /project/{projectID}/copy/refresh400for rejected domain operationEnd-To-End Flows
Open A Checkout Directly
Create A Linked Checkout Copy
Discover A Worktree Created Outside The Application
Remove A Managed Linked Checkout