fix(web): keep repository actions available (#3593)

This commit is contained in:
Carlos Florêncio
2026-09-12 16:29:16 +01:00
committed by GitHub
parent d8eefc5718
commit 3d3209aa1a
26 changed files with 1104 additions and 104 deletions
@@ -9,6 +9,10 @@ status: implemented
## Overview
The [repository creation availability follow-up](../repository-creation-availability/plan.md)
removes the single-row toolbar restriction and preserves the executor for multi-row creation.
The results below record the original implementation.
The original implementation owns directory creation, Git initialization, and workspace repository
persistence, then merges the returned repository into the task-create selector on desktop and
mobile. This follow-up changes the initialization contract so the new repository has one empty root
@@ -10,6 +10,9 @@ spec: "../../specs/workspaces/requirements/create-local-repository.md"
# Task 02: Task-Create Local Repository Selector
The [availability follow-up](../repository-creation-availability/plan.md) replaces
the single-row restriction recorded below and preserves multi-row executors.
## Acceptance
- A task-create repository picker exposes **Create new repository**, and success selects the returned
@@ -0,0 +1,180 @@
---
created: 2026-09-10
status: implemented
requirements:
- REQ-WORKSPACES-CREATE-LOCAL-REPOSITORY-001
system_design:
- ../../specs/workspaces/system-design/create-local-repository.md
legacy_specs: []
---
# Implementation plan: Repository creation availability
## Overview
Expose creation in every editable local repository row. One sequential work order
updates the picker, creation context, success handler, documentation, and focused tests.
## Evidence and root cause
`apps/web/components/task-create-dialog-workspace-repo-chips.tsx:135` supplies
`onCreateRepository` only when `rows.length === 1`. The adjacent refresh callback
has the same condition, explaining why both icons disappear in the screenshot.
The condition counts task rows, not registered repositories. The existing test
`routes repository creation to the only row` supplies two registered repositories
and still exercises creation. `Pill` itself does not condition its action on options.
The old restriction is explicit in the original requirements and implementation
plan. It is no longer suitable for the requested behavior. Backend initialization
already supplies an initial commit, but `applyCreatedLocalRepository` still always
selects a direct-local executor. Removing only the visibility gate would therefore
select an executor that cannot run a multi-repository task.
Reproduction from source: supply two task rows and an opted-in creation callback,
open either local picker, and observe that no creation callback reaches its Pill.
The supplied screenshots corroborate the toolbar difference. No live instance was
mutated and no browser reproduction or product tests ran during this design turn.
## Scope
- Every editable local row offers Refresh and Create across empty, populated, and filtered lists.
- Creation selects the originating row and preserves siblings and multi-row executors.
- Desktop and phone complete the same create-and-submit outcome.
- Update public wording that limits creation to a single row.
Excluded: remote-host creation, locked/edit-mode selectors, Quick Chat opt-in,
backend initialization changes, executor capability
expansion, and a global picker redesign. Retain single-row executor behavior.
## Technical approach
Follow the [system design](../../specs/workspaces/system-design/create-local-repository.md).
Remove the creation and refresh row-count gates in `WorkspaceRepoChips`. Derive
explicit multi-row context in `RepoChips` and pass it to the creation surface.
Separate row/cache updates from optional executor changes in the handler.
Multi-row creation must work without a direct-local profile and preserve the
selected profile; normal submission guards remain authoritative.
Use semantic button queries in tests. Existing negative tests query tooltip text,
which can be absent even when the icon button exists. Cover accessible button
presence and activation instead. Reuse current translation keys when suitable.
## ASCII UI preview
UI-01: Local picker, second task row, populated list.
```text
Before: [Search repositories... ]
[test v]
After: [Search repositories... ] [Refresh] [Create +]
[test v]
```
Both actions stay above the list with zero matches or zero repositories too.
Refresh precedes Create and stays visible but disabled while refreshing.
The illustration expands the icon's accessible label; existing localized copy
and icon remain authoritative.
UI-02: Phone creation flow after tapping the same picker action.
```text
[Create new repository Close]
[Repository name ]
[Parent directory ]
[Target path ]
[Directory list: internal scroll ]
[ Create repository ]
```
Retain the shipped phone Drawer: fixed title/footer, one scroll owner, safe-area
clearance, and touch targets at least 44px. Desktop opens the existing Dialog.
The picker remains viewport-contained. Return focus to the initiating row on
dismissal. Drawings specify order and persistence, not exact spacing.
## Tests
AC references below use prefix `AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001`.
| Evidence | Acceptance |
| --- | --- |
| `task-create-dialog-workspace-repo-chips.test.tsx`: action remains available in every row | .1 |
| Same file: zero/two repository options, zero-match search, caller not opted in | .1 |
| Same file: refresh from each row, unchanged selection, visible disabled loading state | .1 |
| `task-create-dialog-handlers.test.ts`: target-row/cache update, sibling preservation, executor preservation | .7, .8 |
| `create-local-repository-surface.test.tsx`: multi-row creation with no direct-local profile | .8 |
| `task-create-dialog-prop-builders.test.ts`: creation context and locked/edit exclusions | .1, .8 |
The first multi-row button assertion is the required RED test before production edits.
Also cover stale target-row removal and preserve single-row executor-switch tests.
The desktop E2E also exposed a premature loading reset in `useRepositories`.
Its deferred-response regression in `hooks/domains/workspace/use-repositories.test.ts`
proves the cached workspace stays loading until the request finishes (.1).
## E2E tests
Extend `apps/web/e2e/tests/task/create-task-new-local-repository.spec.ts` and
`mobile-create-task-new-local-repository.spec.ts` for .1, .7, and .8. Start with
an existing repository and Worktree, add a second row, create another repository,
assert the first row and executor remain unchanged, and submit. Verify both
persisted repository identities and `main` for the created repository. Reopen a
picker and verify both actions remain available. Refresh from the second row and
assert updated options without changing selections. Keep existing single-row and conflict
scenarios. Phone coverage uses touch and checks action hitbox, drawer containment,
focus return, and no horizontal document overflow.
## Companion plans and documentation
The [original plan](../create-local-repository/plan.md) and its selector work order
record the old restriction as shipped history. Add a follow-up pointer during
implementation; do not rewrite their historical verification results. The
[descriptor repair](../fix-local-repository-darwin-init/plan.md) has no affected
backend work or checks.
Public how-to pages `docs/public/use-kandev.md` and
`docs/public/tasks-and-workflows.md` contain explicit single-row-only wording.
Update them with the implementation, including the multi-row executor distinction.
This design turn updates internal documents only.
## Work orders
- [x] [Task 01: Keep repository creation available](task-01-keep-creation-available.md)
## Verification results
Implementation complete. Exact commands and environment adjustments are recorded
in [Task 01 results](task-01-keep-creation-available.md#results).
- Seven focused Vitest files: 108 tests passed.
- Desktop E2E: 3 tests passed. Phone E2E: 2 tests passed.
- TypeScript, targeted ESLint, localization, and the fresh Vite build passed.
- Public-doc validators passed: 62 tests and 46 pages.
- Specification validation passed: 36 linter tests and all specification files.
- Diff checks passed. No commit or push was requested.
Design validation:
- `python3 scripts/lint-spec-files.test.py`: passed, 36 tests.
- `python3 scripts/lint-spec-files.py --all`: passed.
- `git diff --check`: passed.
- `git status --short -- docs/plans/repository-creation-availability`: confirmed
the new package is untracked; no commit or push was requested.
- Workspace index: 2,655 bytes, within the size limit.
Desktop and phone checks cover the assigned toolbar order, touch sizing, creation
surface, preserved repository rows and executor, refresh loading, and task submission.
Post-PR fixup validation (2026-09-11) also covers submission-bound completion
callbacks and shared repository request ownership: the focused Vitest suite passed
130 tests, `pnpm run typecheck`, `pnpm run i18n:check`, targeted ESLint, the fresh
frontend build, desktop E2E (3 passed), mobile E2E (2 passed), public documentation
validation (62 tests and 46 pages), specification validation (36 tests), and
`git diff --check` all passed.
## Risks
- A direct-local switch after creation would invalidate a multi-row draft.
- Index-based callbacks could select the wrong row after removal; use stable keys.
- Hidden tooltip text is not evidence that an action button is absent.
- Shared surface context must not change existing workspace creation behavior.
@@ -0,0 +1,181 @@
---
id: "01-keep-creation-available"
title: "Keep repository creation available"
status: done
wave: 1
depends_on: []
plan: "plan.md"
requirements:
- REQ-WORKSPACES-CREATE-LOCAL-REPOSITORY-001
acceptance_criteria:
- AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.1
- AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.7
- AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.8
system_design:
- ../../specs/workspaces/system-design/create-local-repository.md
---
# Task 01: Keep repository creation available
## Summary
Expose Refresh and Create in each editable local picker and complete creation into that row.
Preserve multi-row executor selection so the task remains executable.
## In scope
- Creation and refresh callback visibility, explicit form context, target-row/cache updates,
and conditional executor handling described by the system design.
- Component RED test followed by focused implementation and desktop/mobile E2E.
- Public how-to wording, companion-plan follow-up links, and accurate results.
## Out of scope
Remote-host creation, locked/edit selectors, Quick Chat,
backend/API changes, and changing the single-row executor policy.
## Acceptance
1. In opted-in editable local pickers, Refresh and Create remain available in every
row with zero or multiple options and with search matches or no matches (.1).
Refresh updates options without changing selection and stays visible but disabled
while loading. Cover activation and loading on desktop and phone.
2. Success selects only the originating row, preserves siblings and multi-row
executor/profile state, and works without a direct-local profile (.7, .8).
3. Desktop and phone create into a second row and submit using Worktree, while
existing single-row, conflict, and caller-exclusion behavior remains covered.
## ASCII UI preview
UI-01 and UI-02, excerpt from the [full plan](plan.md#ascii-ui-preview):
```text
Picker: [Search repositories...] [Refresh] [Create +]
[Existing repository v]
Phone: [Create new repository Close]
[Name / parent / target path ]
[Directories: internal scroll ]
[ Create repository ]
```
Both actions stay outside filtered options (.1). Desktop opens Dialog; phone opens
the existing safe-area-aware Drawer with 44px touch targets. Dismissal returns
focus to the originating row. Refresh precedes Create in every local row.
## Verification
Run from the repository root. Install once before the first pnpm command in this
worktree. Run the component RED assertion before production edits, then all checks
after implementation. Managed E2E builds fresh assets and tears down its instance.
```bash
(cd apps && pnpm install --frozen-lockfile)
(cd apps/web && pnpm exec vitest run components/task-create-dialog-workspace-repo-chips.test.tsx components/task-create-dialog-handlers.test.ts components/task-create-dialog-prop-builders.test.ts components/create-local-repository-surface.test.tsx components/task-create-dialog-pill.test.tsx components/branch-refresh-button.test.tsx hooks/domains/workspace/use-repositories.test.ts)
(cd apps/web && pnpm run typecheck)
(cd apps/web && pnpm run i18n:check)
(cd apps/web && pnpm e2e:run --project chromium tests/task/create-task-new-local-repository.spec.ts)
(cd apps/web && pnpm e2e:run --project mobile-chrome tests/task/mobile-create-task-new-local-repository.spec.ts)
node --test scripts/validate-public-docs.test.mjs
node scripts/validate-public-docs.mjs
python3 scripts/lint-spec-files.test.py
python3 scripts/lint-spec-files.py --all
git diff --check
git status --short -- docs/plans/repository-creation-availability
```
Add any changed additional test file to the focused command before marking done.
Record actual command results and E2E scenario counts, including RED evidence.
## Files likely touched
- `apps/web/components/task-create-dialog-workspace-repo-chips.tsx` and its test.
- `apps/web/components/task-create-dialog-repo-chips.tsx`.
- `apps/web/components/task-create-dialog-prop-builders.ts` and its test.
- `apps/web/components/task-create-dialog-types.ts`.
- `apps/web/components/task-create-dialog-handlers.ts` and its test.
- `apps/web/components/create-local-repository-surface.tsx` and its test.
- `apps/web/components/task-create-dialog-pill.tsx` and its test (action sizing).
- `apps/web/components/branch-refresh-button.tsx` and its test (matching toolbar sizing).
- The two E2E files named in Verification; a shared helper if needed.
- `apps/web/hooks/domains/workspace/use-repositories.ts` and its test: remove the
premature cached-data loading reset identified by the desktop regression.
- `apps/web/src/locales/*/common.json` only if existing copy cannot express the context.
- `docs/public/use-kandev.md`, `docs/public/tasks-and-workflows.md`.
- `docs/plans/create-local-repository/plan.md`, `task-02-task-create-selector.md`.
- This package and its linked requirement/design for results and status.
## Dependencies
None. The backend already returns a repository with a real initial `main` commit.
## Risks
Do not pass a multi-row draft through the direct-local executor setters. Do not
overwrite sibling branch/policy choices. Re-check the target key before applying
an asynchronous result; a removed row must not redirect the new selection.
## Parallelism
`sequential`
## Inputs
- [Requirements](../../specs/workspaces/requirements/create-local-repository.md).
- [System design](../../specs/workspaces/system-design/create-local-repository.md).
- Existing selector tests, handler tests, and desktop/mobile creation E2E.
- `/tdd`, `/mobile-parity`, `/e2e`, and `apps/web/AGENTS.md`.
## Results
Completed on 2026-09-10.
- Dependency installation passed with the frozen lockfile.
- The seven-file Vitest command above passed: 108 tests.
- `pnpm run typecheck` passed after the final production changes.
- Targeted ESLint passed with no warnings on changed production files and the E2E helper.
- `pnpm run i18n:check` passed. Existing unused-key warnings remain informational.
- `node --test scripts/validate-public-docs.test.mjs` passed: 62 tests.
- `node scripts/validate-public-docs.mjs` passed: 46 pages.
- `python3 scripts/lint-spec-files.test.py` passed: 36 tests.
- `python3 scripts/lint-spec-files.py --all` and `git diff --check` passed.
- The status check confirmed the new work order and plan. Files remain uncommitted.
RED evidence: the second-row toolbar test failed on the missing Refresh button;
creation tests failed without a direct-local profile; the deferred refresh test
proved premature loading reset; the sizing test detected the oversized desktop
Refresh control. Each corresponding focused test passed after its correction.
The initial full managed build was interrupted during unrelated cross-platform
compilation under host memory pressure. A host-only build then encountered a full
shared `/tmp` volume. An owned temporary directory on the workspace volume resolved
the disk constraint. No shared data or other processes were removed.
The successful build and browser commands, from the repository root, were:
```bash
TMPDIR=/root/kandev-repository-picker.IKmBOS GOTMPDIR=/root/kandev-repository-picker.IKmBOS GOMAXPROCS=2 GOFLAGS="-p=2" make -C apps/backend build-dev e2e-plugin-package
(cd apps && pnpm --filter @kandev/web build:e2e)
(cd apps/web && TMPDIR=/root/kandev-repository-picker.IKmBOS GOMAXPROCS=2 pnpm e2e:run --host --no-build --project chromium tests/task/create-task-new-local-repository.spec.ts)
(cd apps/web && TMPDIR=/root/kandev-repository-picker.IKmBOS GOMAXPROCS=2 pnpm e2e:run --host --no-build --project mobile-chrome tests/task/mobile-create-task-new-local-repository.spec.ts -- --timeout=180000)
```
The temporary path above records this run. Create a new owned directory on a volume
with available space before repeating those environment overrides.
Final desktop result: 3 passed (53.9s). Final phone result: 2 passed (31.9s).
The phone retry increased only the total test budget after the busy host exhausted
the default 60 seconds. Assertion and causal-wait timeouts were unchanged.
The frontend was rebuilt after each production correction; `--no-build` reused
those fresh assets and the verified host backend. Isolated runtimes were torn down,
and the owned temporary compile/Playwright caches were removed after verification.
PR fixup scope: bind each asynchronous creation completion to its submission target
and keep repository loading markers correct when cancelled or concurrent requests
overlap. Focused Vitest coverage now includes stale-surface dismissal and shared
request ownership; all changes remain within the existing local repository flow.
Post-fixup validation passed on 2026-09-11: focused Vitest (8 files, 130 tests),
typecheck, i18n checks, targeted ESLint, fresh frontend build, desktop E2E (3),
mobile E2E (2), public-doc validators (62 tests and 46 pages), specification
validators (36 tests), and `git diff --check`.
+4 -2
View File
@@ -83,7 +83,7 @@ Use **New Task** in the sidebar. In an open task, the **Task** split button also
| Source | Use it for | Important behavior |
| ---------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Repo** | A configured, discovered, or new local repository | Select a named branch policy or a raw base branch for each repository row. A policy creates a fresh branch from its saved base and uses its branch template. For a single-row new task, **Create new repository** initializes an empty `main` repository with one empty initial commit in a parent folder you choose. Add more rows for a multi-repository task. |
| **Repo** | A configured, discovered, or new local repository | Select a named branch policy or a raw base branch for each repository row. A policy creates a fresh branch from its saved base and uses its branch template. Each editable local row offers **Refresh repositories** and **Create new repository**. Creation initializes `main` with one empty initial commit in a parent folder you choose. Add more rows for a multi-repository task. |
| **Remote** | A remote repository | Search configured GitHub, GitLab, or Azure DevOps repositories, or paste a supported URL. A pasted URL stays editable until you press Enter; then select the branch. Anonymous, credential-free reads include public GitHub repository branches, pull requests, and issues, plus public `gitlab.com` branch discovery. Private resources and authenticated browse/write features require valid provider credentials. |
| **None** | Planning, research, or work outside Git | Use a scratch workspace or an optional folder on the Kandev host. Git worktree execution and repository-aware Changes, branch, and pull-request features are unavailable. |
@@ -120,7 +120,9 @@ candidate. Kandev does not switch candidates after an ambiguous started turn.
If the route has no eligible candidate, wait for the current turn to settle and
use the session's **Retry current agent** or **Try next agent** recovery action.
Creating a repository is available only in an unlocked, single-repository **New Task** form. Kandev rejects an existing target path, creates one empty initial commit but no project files, registers the repository in the workspace, and switches the task to a direct **Local** executor profile. If no direct Local profile is available, repository creation stays disabled. Add more repository rows only after selecting existing repositories; empty multi-repository worktrees are not supported.
Every editable local repository row in **New Task** offers **Refresh repositories** and **Create new repository**, including populated lists and empty search results. Refresh updates the available repositories without changing your selections. It stays visible but disabled during the request.
Kandev rejects an existing target path and creates one empty initial commit with no project files. It registers the repository in the workspace and selects it in the originating row. For a single-row task, Kandev selects a direct **Local** profile. If no direct Local profile exists, single-row creation stays disabled. For multiple rows, creation preserves the selected executor and the other rows. It does not require a direct Local profile.
### Work with an empty remote repository
+2 -2
View File
@@ -151,7 +151,7 @@ Remote repository and issue/PR URLs are not added from this settings page. Use t
Detected capabilities come from the installed CLI and can change after an agent upgrade or login. A model or mode shown in documentation is not guaranteed for every account. If no built-in adapter fits, **Add TUI Agent** creates a passthrough integration; passthrough has a different resume, usage, and MCP contract from an ACP-capable agent.
For the first task on an existing repository, keep the seeded **Worktree** executor profile. It creates a separate Git checkout so concurrent Kandev tasks do not edit the same working tree. A worktree is Git isolation, not operating-system isolation. Choose **Local** only when direct edits in the selected checkout are intentional. A repository initialized from **New Task** already has one empty initial commit and no project files, and Kandev selects a direct Local profile for that flow. See [Agents and profiles](agents-and-profiles.md) and [Executors](executors.md) before using Docker, SSH, Sprites, custom scripts, or shared infrastructure.
For the first task on an existing repository, keep the seeded **Worktree** executor profile. It creates a separate Git checkout so concurrent Kandev tasks do not edit the same working tree. A worktree is Git isolation, not operating-system isolation. Choose **Local** only when direct edits in the selected checkout are intentional. A repository initialized from **New Task** has one empty initial commit and no project files. Creation selects a direct Local profile for a single-row task when one is available and preserves the selected executor for multiple rows. Without a direct Local profile, single-row creation remains disabled. See [Agents and profiles](agents-and-profiles.md) and [Executors](executors.md) before using Docker, SSH, Sprites, custom scripts, or shared infrastructure.
## Create and start the first task
@@ -174,7 +174,7 @@ If another actor initializes the remote first, Kandev stops without replacing th
The dialog also supports multiple repositories, remote GitHub rows, and a single-repository **Fork a new branch** option when the Local executor is selected. Important boundaries:
- Multi-repository tasks require the Worktree executor in the current task-create path.
- Creating an empty repository is available only for a single repository row and switches the task to a direct Local profile.
- Every editable local repository row offers **Refresh repositories** and **Create new repository**, regardless of existing repositories or search results. Creation preserves the executor for multiple rows and selects a direct Local profile for a single row.
- No-repository tasks cannot use the Worktree executor.
- The local fork option is off by default and requires explicit consent before Kandev discards dirty source-checkout changes.
- Agent and executor choices can be disabled when their remote credential requirements are incompatible.
+1
View File
@@ -47,6 +47,7 @@ Git state.
### System design
- [Local repository creation](system-design/create-local-repository.md)
- [Secret reference protection](system-design/repository-secrets.md)
- [Empty Remote Repositories](system-design/empty-remote-repositories.md)
- [Improve Kandev](system-design/improve-kandev.md)
@@ -2,7 +2,7 @@
status: active
system: workspaces
created: 2026-07-21
updated: 2026-08-05
updated: 2026-09-10
owners:
- kandev
---
@@ -20,14 +20,21 @@ Users can create tasks from an existing local or remote repository, but a new pr
#### Acceptance criteria
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.1:** The local repository selector in **Create New Task** exposes a visible **Create new repository** action while the task has exactly one repository row.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.1:** Every editable local repository row in **Create New Task** exposes visible **Refresh repositories** and **Create new repository** actions on desktop and mobile. Availability does not depend on the number of task rows, registered or discovered repositories, or search matches. Refresh updates the repository list without changing selected rows; it remains visible but disabled while a refresh is in progress.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.2:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.3:** A manually entered parent path may contain missing trailing directories. Confirmation creates those directories beneath the nearest accessible, trusted ancestor before initializing the repository.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.4:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.5:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.6:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.7:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.8:** 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.
- **AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.8:** For a single repository row, the task-create flow retains its direct-local executor selection and explanation. For multiple rows, creating a repository preserves the selected executor profile and all sibling rows. The absence of a direct-local profile does not block repository creation in a multi-row task; normal task execution compatibility checks still apply.
## Compatibility update
The September 2026 picker change removes the former single-row creation restriction.
New repositories already contain an initial commit, so they can supply a worktree base.
The action remains scoped to editable local selectors that support creation.
Remote-host repository creation and locked repository selectors are outside this change.
## Migrated source detail
@@ -40,7 +47,7 @@ repository on the machine running Kandev, select it, and continue composing the
## What
- The local repository selector in **Create New Task** exposes a visible **Create new repository**
action while the task has exactly one repository row.
action in every editable local repository row, including populated and filtered lists.
- 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
@@ -58,11 +65,11 @@ repository on the machine running Kandev, select it, and continue composing the
- 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 single-row 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.
- Multi-repository task creation exposes the action in every editable local row and preserves
the selected executor. The new repository already 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
@@ -153,7 +160,7 @@ to the created canonical target path and the active workspace.
| `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. |
| No compatible direct local executor profile is available for a single-row task | The form explains the requirement and does not call the initialization endpoint. Multi-row creation does not require a direct-local profile. |
## Persistence Guarantees
@@ -187,14 +194,16 @@ workspace registration.
`/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
- **GIVEN** a single-row task has a worktree or container executor profile 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,
- **GIVEN** a single-row task has no direct local executor profile 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** a task with multiple repository rows, **WHEN** the user opens an editable local picker,
**THEN** **Create new repository** remains available. Successful creation selects the new
repository in that row without changing sibling rows or the executor profile.
- **GIVEN** an empty, populated, or search-filtered repository list, **WHEN** the user opens an
editable local picker, **THEN** the creation action remains visible outside the search results.
- **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.
@@ -228,7 +237,6 @@ workspace registration.
- 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
@@ -240,5 +248,9 @@ workspace registration.
See [Create Local Repository plan](../../../plans/create-local-repository/plan.md).
The picker availability extension uses the
[repository creation availability plan](../../../plans/repository-creation-availability/plan.md)
and [system design](../system-design/create-local-repository.md).
The descriptor-bound macOS repair is tracked in the
[local repository descriptor initialization plan](../../../plans/fix-local-repository-darwin-init/plan.md).
@@ -0,0 +1,108 @@
---
status: current
system: workspaces
requirements:
- REQ-WORKSPACES-CREATE-LOCAL-REPOSITORY-001
---
# Local repository creation system design
## Ownership and scope
Workspaces owns repository initialization and registration. Task creation consumes the
returned repository and selects it in the originating row. This design extends picker
availability and preserves the existing initialization API and filesystem guarantees.
## Requirement mapping
| Acceptance criteria | Design boundary |
| --- | --- |
| AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.1 | Picker availability |
| AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.7 | Row selection and cache update |
| AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.8 | Executor handling |
| AC-WORKSPACES-CREATE-LOCAL-REPOSITORY-001.2 through .6 | Existing initialization contract |
## Picker availability
`WorkspaceRepoChips` passes the optional creation callback to each `RepoChip`, bound
to its stable row key. Remove the `rows.length === 1` gates from both creation
and refresh callbacks. Forward the shared refresh callback to every row.
`Pill` already renders the action outside filtered options; retain that placement.
Repository inventory, selected values, and search matches do not gate the action.
Keep caller opt-in and the create-mode/unlocked checks in
`localRepositoryCreationEnabled`. Keep both toolbar actions outside search results.
Refresh retains its existing callback, loading spinner, and in-flight disablement;
it does not change row selections. Preserve optional callbacks for other consumers.
`useRepositories` leaves loading completion to the response/store update and request
finalizer. Cached data is not evidence that an active refresh has finished. Remove
the effect that clears loading solely because the workspace is already loaded. Each
automatic or manual request owns one loading marker; cancellation releases that
marker immediately, while concurrent requests keep the workspace loading until the
last request settles.
## Row selection and cache update
`RepoChips` records the originating key in `creatingForRowKey` and opens
`CreateLocalRepositorySurface`. On success, `applyCreatedLocalRepository` selects
the returned ID and `main`, clears local-path selection, and upserts the workspace
cache. Preserve every sibling row and unrelated draft field. Apply the normal
repository-change clearing of stale branch-policy metadata to the target row.
If the originating row disappears before completion, retain the created repository
in the workspace cache but do not replace another row or change its executor.
The creation surface captures the row callback at submission. A completion for an
older request still updates that stable row/cache handler, but it does not dismiss a
newer creation surface opened for another row.
## Executor handling
Retain the existing single-row direct-local selection policy. For multiple rows,
preserve the selected executor/profile and omit executor setters and executor
last-used writes from repository creation. Do not require
`findDirectLocalExecutorProfile` to return a profile for this path.
Thread an explicit multi-row creation context through the existing creation props
and surface. Its readiness and notice describe repository initialization without
promising a switch to Local. Reuse translated generic initialization copy when
accurate; add localized copy only if necessary. Do not disguise task creation as
the existing `workspace` context. Use current form state when applying a response.
Task submission retains the established executor compatibility validation, including
`getMultiRepoExecutorDisabledReason`. Creation does not broaden executor capabilities.
## Desktop and phone composition
The shared search popover remains a short, touch-usable selector. Its creation
button opens the existing desktop Dialog or phone Drawer. The nearest shipped
example is `CreateLocalRepositorySurface`; `MobilePickerSheet` supplies the drawer
precedent. Keep search and the action above the internally scrolling option list.
The phone form retains its name/path hierarchy, one directory-list scroll owner,
dynamic viewport height, safe-area footer, and return focus to the originating
selector. Creation remains a temporary step inside task composition. Keep actions
reachable without hover and at least 44px on touch surfaces. Reuse the shared
control-sizing helper for the affected action's 28px fine-pointer desktop size.
No global picker redesign is part of this change.
## Existing initialization contract
`initializeLocalRepository` calls the workspace-scoped `initialize-local` endpoint.
The backend creates the validated target and an empty initial commit on `main`,
then registers the repository. Conflicts do not modify an existing target.
Form errors retain inputs; dismissal before submission creates nothing. Successful
repositories persist independently of task cancellation. No API, schema, trust,
or backend initialization change is required.
The [explicit local repository trust decision](../../../decisions/2026-07-20-explicit-local-repository-trust.md)
continues to govern exact-path registration and access.
## Validation and delivery
Use component coverage for inventory/search/row-count combinations and callback
identity. Handler and form tests cover executor preservation and absence of direct
profiles. Desktop and phone E2E create into a second row, preserve the first row,
and submit with Worktree. Both viewports also refresh from the second row and
verify updated repository options without changing selections. Cover visible,
disabled refresh during loading. Preserve single-row and conflict coverage.
See the [implementation plan](../../../plans/repository-creation-availability/plan.md).