Files
kandev/docs/specs/create-local-repository/spec.md
T
2026-08-05 22:34:26 +01:00

13 KiB

status, created, updated, owner
status created updated owner
shipped 2026-07-21 2026-08-05 kandev

Create a Local Repository During Task Creation

Why

Users can create tasks from an existing local or remote repository, but a new project requires leaving the task flow to initialize and register a Git repository first. Users need to create that repository on the machine running Kandev, select it, and continue composing the same task.

What

  • The local repository selector in Create New Task exposes a visible Create new repository action while the task has exactly one repository row.
  • The creation form asks for a repository name and an absolute parent path. It shows the resulting target path before confirmation. The name is one platform-native path segment, not a path.
  • A manually entered parent path may contain missing trailing directories. Confirmation creates those directories beneath the nearest accessible, trusted ancestor before initializing the repository.
  • The parent-folder browser lists the filesystem of the machine running Kandev and shares the same directory browsing behavior as the None source mode's starting-folder picker. It also lets the user create and enter one new child folder in the currently displayed directory.
  • Confirmation creates <parent>/<name> only when that target does not already exist, initializes a local Git repository with default branch main, and creates one empty initial commit on that branch. The working tree contains no user files.
  • Git initialization remains bound to the exact request-owned directory identity that the backend opened and verified. Replacing its pathname before or during initialization must not cause Git to modify the replacement path.
  • A successfully initialized repository is persisted as a local repository in the active workspace, added to the in-memory workspace repository list, selected in the originating row, and displays main as that row's branch. Other task fields and repository rows are unchanged.
  • The task-create flow continues to select a compatible local or local_pc executor profile for the first task when the selected profile cannot run the newly created local repository, and explains the change. Executor selection is unchanged by the initial baseline commit.
  • Multi-repository task creation does not expose the action because those tasks require the worktree executor. Users can add the repository to a multi-repository task after it has its first commit.
  • Initialization is an explicit local-path trust grant for the exact canonical repository path. It does not widen automatic discovery roots or grant access to sibling paths.
  • The form remains open with the entered name and parent folder when initialization fails, surfaces an actionable error, and does not select a repository. Editing either input or navigating to another folder clears the stale submission error immediately.
  • Closing the creation form before confirmation does not create a directory or repository record.
  • Desktop and mobile provide the same capability. Mobile uses a touch-native picker surface with no hover-only action and keeps repository name, target path, errors, and the primary action reachable.

The explicit local repository trust contract remains defined by Local Workspace Repositories and ADR-2026-07-20.

Data Model

No new database entity is introduced. A successful initialization creates one existing repositories record:

Field Contract
workspace_id The active task-creation workspace.
name The user-supplied repository name after trimming.
source_type local.
local_path The canonical absolute <parent>/<name> path.
default_branch main.

The filesystem repository and the repositories row are created as one product operation when the repository form is confirmed, before the task is submitted. The created Git repository contains one empty root commit reachable from refs/heads/main, but no user files in the working tree. A successful response means both exist; a failed response must not leave a repository row. The filesystem cleanup behavior for a partial failure is described under Failure modes.

API Surface

POST /api/v1/workspaces/:id/repositories/initialize-local

Request:

{
  "name": "new-project",
  "parent_path": "/home/user/projects"
}

Response: 201 Created with the existing Repository DTO. The response has source_type: "local", the canonical local_path, and default_branch: "main".

The backend, not the browser, is authoritative for validation and filesystem changes:

  • 400 Bad Request: missing/invalid name, relative parent path, inaccessible parent, parent is not a directory, or invalid request body.
  • 404 Not Found: workspace does not exist.
  • 409 Conflict: <parent>/<name> already exists. Existing directories are never converted or overwritten by this operation.
  • 500 Internal Server Error: Git initialization or repository persistence fails.

POST /api/v1/fs/create-dir

Request:

{
  "parent_path": "/home/user/projects",
  "name": "team"
}

Response: 201 Created with the same directory-listing shape as GET /api/v1/fs/list-dir, rooted at the new folder. Invalid names or inaccessible parents return 400 Bad Request; an existing child returns 409 Conflict.

Permissions

This follows Kandev's trusted-local-user model. The user may initialize a repository under any explicitly selected parent path whose nearest existing ancestor the Kandev process can trust and write. Missing trailing directories are created as process-owned directories. The grant is scoped to the created canonical target path and the active workspace.

Failure Modes

Condition Observable behavior
Name is empty, ./.., or contains a host path separator Confirmation is disabled client-side and the backend rejects a forged request.
The nearest existing parent ancestor cannot be trusted, read, or written The form stays open, shows an error, and no repository is selected or persisted.
Target already exists, including an empty directory The request returns conflict and does not modify the target.
The request-owned staging pathname is replaced during Git initialization The operation fails without modifying the replacement path. Cleanup remains limited to a request-owned directory whose identity can still be verified.
git is unavailable, initialization fails, or the initial commit fails The request fails, no repository row is persisted, and Kandev removes only the target directory created by this request when cleanup is safe. A cleanup failure is logged and the error remains visible.
Repository persistence fails after Git initialization or the initial commit The request fails and performs the same best-effort cleanup of the request-owned target; no repository row is returned.
Frontend state refresh fails after a successful response The returned repository is still selected directly and merged into the active workspace cache without waiting for a second list request.
No compatible direct local executor profile is available for the existing task-create policy The form explains the requirement and does not call the initialization endpoint.

Persistence Guarantees

The initialized Git repository, including its empty baseline commit, is ordinary user-owned filesystem content and is never tied to task deletion. Its existing repositories record survives backend restart. Canceling, archiving, or deleting the task does not delete the repository or its workspace registration.

Mobile Design Contract

  • Entry and outcome: the repository chip opens the same local repository picker as desktop; a visible, at-least-44px Create new repository row opens the creation flow and returns the new selection to the originating chip.
  • Nearest exemplar: MobilePickerSheet supplies the inset bottom-drawer geometry, fixed header, internal scrolling, and predictable dismissal used by task-workbench pickers.
  • Hierarchy and primary action: repository name and current target path stay above the directory list; Create repository is the single primary action in the safe-area-aware footer.
  • Presentation rationale: repository creation is a short temporary choice inside task creation, so mobile uses an inset bottom drawer rather than a new route or a compressed desktop dialog.
  • Geometry: the drawer uses one min-h-0 vertical scroll owner, dynamic viewport sizing, bottom safe-area clearance, no document horizontal overflow, and 44px directory/action rows. Keyboard focus remains visible and dismissal returns focus to the repository selector.
  • Shared logic: validation, target-path derivation, directory listing, API mutation, workspace cache update, row selection, and compatible local-executor selection are shared. Only Dialog-versus-Drawer composition is responsive.

Scenarios

  • GIVEN the task dialog has a local repository row, WHEN the user chooses Create new repository, selects /work/projects, enters alpha, and confirms, THEN /work/projects/alpha contains a Git repository with one empty initial commit on main and no user files, the workspace has a matching local repository record, the branch endpoint returns a local main option, and the originating task row selects alpha / main.
  • GIVEN a worktree or container executor profile is selected and a direct local profile is available, WHEN repository initialization succeeds, THEN the form selects the compatible direct local profile and tells the user why the executor changed.
  • GIVEN no direct local executor profile is available, WHEN the user opens the creation form, THEN the primary action explains the requirement and no directory or repository is created.
  • GIVEN a task with multiple repository rows, WHEN the user opens any repository picker, THEN Create new repository is not offered because the action remains limited to the existing single-row task-create flow.
  • GIVEN /work/projects/alpha already exists, WHEN the user tries to create alpha under /work/projects, THEN the UI reports the conflict and Kandev does not modify or register the existing path.
  • GIVEN the backend has opened its request-owned staging directory, WHEN that directory is renamed and another directory is installed at the staging pathname before Git starts, THEN Git operates only on the originally opened directory, the replacement remains unchanged, and the request fails because the original identity is no longer publishable at that pathname.
  • GIVEN /work exists but /work/team/projects does not, WHEN the user enters /work/team/projects as the parent and creates alpha, THEN Kandev creates both missing parent directories and initializes /work/team/projects/alpha.
  • GIVEN the folder navigator displays /work, WHEN the user creates a child named team, THEN /work/team is created and the navigator enters that folder on desktop and mobile.
  • GIVEN an initialization error is visible, WHEN the user edits the name or parent path or navigates to another folder, THEN the previous error disappears before the next submission.
  • GIVEN an invalid name or unwritable parent, WHEN the user confirms, THEN the form stays open with its inputs intact and no repository is selected.
  • GIVEN the creation form is open, WHEN the user dismisses it without confirming, THEN no filesystem or database change occurs and their task draft remains intact.
  • GIVEN a repository was initialized and selected, WHEN the user later cancels or deletes the task, THEN the repository directory and workspace repository record remain.
  • GIVEN a Pixel 5 viewport, WHEN the user completes the same flow, THEN all actions are reachable by touch, the directory list scrolls inside the drawer, the footer clears the safe area, and the page has no horizontal overflow.

Out of Scope

  • Creating or publishing a remote Git hosting repository.
  • Cloning, importing, or converting an existing directory; existing paths continue through the existing local repository selection and settings flows.
  • README, license, .gitignore, language template, remote, visibility, or hosting-provider setup.
  • Choosing the initial branch name; new repositories always start on main with the empty baseline commit described above.
  • Worktree, container, or remote execution before the repository has its first commit.
  • Creating a new repository as part of a multi-repository task.
  • Adding the creation action to Quick Chat, repository settings, project repository pickers, or MCP tools in this iteration.
  • Automatically deleting a successfully created repository when its originating task is canceled or deleted.
  • Adding local-repository initialization support to BSD platforms that do not currently implement trusted directory ownership checks and exclusive publication.

Implementation Plan

See Create Local Repository plan.

The descriptor-bound macOS repair is tracked in the local repository descriptor initialization plan.