mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-13 13:18:56 +02:00
main
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
77a05fb731 |
Revert "feat(daemon): worktree_pool mode for local_directory (MUL-3483) (#4986)" (#5037)
This reverts commit
|
||
|
|
7bb8076ed0 |
feat(daemon): worktree_pool mode for local_directory (MUL-3483) (#4986)
* feat(daemon): add worktree_pool mode for local_directory (MUL-3483) ## What changed Squad workflows bound to the same `local_directory` resource used to serialise on a single path mutex — a documented pain point from GitHub issue #4377. This introduces an opt-in `worktree_pool` mode on the `local_directory` project resource. When enabled, each task gets its own `git worktree add` under a daemon-managed pool root, so sibling tasks on the same base repo now run truly in parallel while `git worktree add/remove/prune` stays serialised behind a per-repo mutex. ## Shape - `local_directory.resource_ref` gains three optional fields: `mode` ("in_place" default / "worktree_pool"), `pool_root` (defaults to `<parent>/.multica-worktrees/<base>`), `max_parallel` (defaults to 4). Legacy rows are byte-identical after round-trip: the server validator strips the pool fields on the default in_place path so older clients keep behaving exactly as before. - New `WorktreePoolManager` (`server/internal/daemon/worktree_pool.go`) owns pool allocation, per-repo git-metadata mutex, and cleanup. - `acquireLocalDirectoryLockIfNeeded` now branches on the ref's mode. in_place stays on `LocalPathLocker` and the shared tree; worktree_pool routes through the pool manager, publishes a lease keyed by task ID, and pins the agent to the freshly allocated worktree in `execenv.PrepareParams.LocalWorkDir`. - Pool saturation is a structured wait_reason (`worktree_pool saturated (N/M) on <path> (holders: ...)`), retrying on the existing cancel-poll interval — same UX as the historical path-mutex wait. ## Safety guardrails (also known footguns from prior art) - Repos with initialised submodules are refused up front. Multi-checkout of a superproject is explicitly unsupported by `git worktree(1)` BUGS and the per-worktree `modules/` directories bloat disk by pool size ×. - Dirty worktrees are NEVER `--force` removed on release. If the agent left uncommitted changes behind we keep the directory (and free the slot) so users can inspect. This is the failure mode claude-code#55724 documented and the pool must not regress into. - The per-repo mutex covers every `git worktree add/remove/prune` and `submodule status` invocation for a given base, matching the in-process-queue fix Anthropic settled on for claude-code#34645 (`.git/config.lock` races on concurrent add). - Task UUID is the source of truth for both branch (`multica/<uuid>`) and worktree path (`<pool_root>/<uuid>`) so a single agent running multiple worker tasks in parallel can never collide. - Non-empty leftover directories at the target path abort the allocation instead of silently starting the agent in an unknown state. ## Explicit MVP non-goals (deferred, tracked as follow-up work) - Windows worktree-remove retry (permission-denied on locked handles). - Detached-HEAD fast path for read-only exploration tasks. - `post-checkout` hook opt-out / serialisation. - Automatic `git lfs install`. - UI surfacing of the pool state / dirty worktree list. ## Tests - `worktree_pool_test.go` (new): full acquire→release lifecycle, parallel allocation, saturation with holder list, slot re-use after release, dirty-worktree preservation, concurrent-acquire serialisation (the config.lock guard), submodule refusal, missing base rejection, pool root auto-mkdir, non-empty leftover refusal, ctx cancel. - Handler validator gains three rejection cases (unknown mode, relative pool_root, negative max_parallel) and a round-trip test that pins the normalised JSON shape for both modes. - Daemon `localDirectoryRef` helpers get a defaults test and the pool root path derivation is pinned. ## Wire-compat and rollout - Default off. Existing rows keep the historical shape (no `mode`, `pool_root`, or `max_parallel` in the JSON) and behave exactly as before. - Opt-in via `--ref '{"local_path":"...","daemon_id":"...","mode":"worktree_pool"}'` today. CLI flag shortcuts (`--mode`, `--pool-root`, `--max-parallel`) can follow in a small tail PR — not blocking. - No DB migration. No UI change required. Co-authored-by: multica-agent <github@multica.ai> * feat(daemon): address worktree_pool review nits (MUL-3483) Follow-up to #4986. Three non-blocking review points from GPT-Boy: 1. **Daemon integration test for lease → runTask plumbing.** `TestAcquireLocalDirectory_WorktreePoolPublishesLease` (and its in_place counterpart) pin the exact contract runTask relies on when it reads `d.localLeases.Load(task.ID)` and feeds `lease.WorkDir` into `execenv.PrepareParams.LocalWorkDir`. A future refactor that drops the Store, mistypes the key, or swaps back to `assignment.AbsPath` on the pool branch will now fail here rather than silently defeat the whole point of worktree_pool mode. 2. **Untracked-only dirty case now classifies as dirty.** `worktreeIsDirty` used `--untracked-files=no`, which meant a worktree with only untracked files was reported "clean" and hit the `git worktree remove` branch — git itself would then refuse the removal because the file exists (so no data was lost), but the log path lied about what happened on disk. Switching to `--untracked-files=normal` routes agents' fresh drafts directly through the "leaving on disk for user inspection" branch, and `TestWorktreePool_UntrackedOnlyIsKept` pins the guarantee so nobody quietly reverts the flag later. 3. **Skill doc note on default `pool_root` location.** `multica-projects-and-resources/SKILL.md` now spells out the three new ref fields (`mode`, `pool_root`, `max_parallel`), the default `<parent>/.multica-worktrees/<repo>` location (next to the repo, not inside it), the write-permission requirement on the parent directory, and the submodule restriction — so agents advising self-host users hit the right doc line rather than reading source. Existing test suite still green: - `go vet ./...` clean - `go test ./internal/daemon/... ./internal/handler/... ./internal/service/...` all pass Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Eve <eve@multica-ai.local> Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
20eecfb093 | fix(projects): honor repo resource checkout refs (MUL-3593) (#4470) | ||
|
|
341ce7bfa5 |
feat: support local working directory for projects (MUL-2618 v1) (#3283)
* feat(project): add local_directory project_resource type (MUL-2662)
Adds a second project_resource type alongside github_repo so a project
can be pinned to an existing directory on a specific daemon (the v1 of
the local-working-directory flow tracked in MUL-2618). The ref schema is
{ local_path, daemon_id, label? }; local_path must be absolute and
daemon_id is required. The same (daemon_id, local_path) pair is allowed
on multiple projects by design — no UNIQUE constraint is added.
Implementation reuses the existing project_resource API surface: the new
type is wired through the validator switch with no migration, no new
events, and no daemon-handler changes (daemon already passes through
arbitrary resource types via ProjectResources). The CLI gains
--local-path / --daemon-id / --ref-label shortcuts so
`multica project resource add --type local_directory` mirrors the
existing `--type github_repo --url ...` ergonomics; the generic --ref
flag still works for both types.
Tests cover the full CRUD lifecycle, the same-path-across-projects
allowance, the same-path-same-project conflict, the validator rejections
(missing/blank/relative path, missing daemon_id, wrong payload type),
and the cross-platform isAbsoluteLocalPath helper.
Co-authored-by: multica-agent <github@multica.ai>
* feat(project): add update endpoint + label-shadow guard for project_resource (MUL-2662)
Addresses the Elon review on PR #3263:
- Add PUT /api/projects/{id}/resources/{resourceId} with sqlc query,
matching handler, CLI `project resource update`, and a new
EventProjectResourceUpdated WS event. resource_type stays immutable;
ref/label/position are all individually optional.
- Catch same-project (daemon_id, local_path) collisions where only the
embedded label differs — the row-level UNIQUE only matches the full
ref JSON, so a label typo would otherwise let the same working
directory bind twice.
- Tests cover the update lifecycle (label-only / ref / clear / 404 /
invalid path) and the label-shadow conflict on both create and
update; the in-place rename still succeeds because the conflict
scan ignores the row being edited.
Incidental: regenerating sqlc picked up a missing skills_local scan in
UpdateAgentCustomEnv that drifted in from #3200.
Co-authored-by: multica-agent <github@multica.ai>
* fix(project): close bundled-create label-shadow gap + merge resource_ref on CLI update (MUL-2662)
Two follow-ups from MUL-2662 review round 2:
- CreateProject inline resources path now dedupes local_directory entries on
(daemon_id, local_path) before opening the transaction. The DB-level
UNIQUE(project_id, resource_type, resource_ref) constraint only fires on a
full JSON match, so two rows with the same target but different `label`
would otherwise slip past. Standalone POST/PUT already cover this via
findLocalDirectoryConflict; bundled create was the missing surface.
- `multica project resource update` now seeds resource_ref from the existing
row before applying per-type shortcut flags, so `--default-branch-hint x`
on its own no longer constructs a payload missing `url` (which the server
400s on). Local_directory partial edits get the same merge behavior.
Co-authored-by: multica-agent <github@multica.ai>
* feat(desktop): local_directory project_resource UI (MUL-2665) (#3273)
* feat(desktop): local_directory project_resource UI (MUL-2665)
First UI surface for the local-working-directory flow tracked in MUL-2618.
Lets users on the desktop pin a project to an existing folder on this
machine; web stays read-only since the per-daemon check can't be done in
the browser.
What's new for the renderer:
- ProjectResourcesSection grows a desktop-only "Add local directory"
button next to the existing GitHub-repo popover. Clicking it opens
Electron's native folder picker, validates the path through a new
IPC pair (existence + r/w), and submits a project_resource of
resource_type=local_directory with daemon_id pulled live from
daemonAPI.getStatus.
- LocalDirectoryRow renders the rename pencil + path tooltip, and
greys out when ref.daemon_id != this machine's daemon_id (with a
"only available on the machine that registered this directory"
tooltip). Delete stays enabled so users can drop stale registrations
from any device.
- LocalDirectoryHint sits above the issue-detail comment composer and
shows "Agent will work in-place at {label} ({path})" when the issue's
project has a local_directory matching this daemon. Hidden on web.
- TaskStatusPill picks up a new "waiting_for_directory_release" stage
that the daemon will publish when it dequeues a task but can't
acquire the path lock. The render is in place now so the daemon
sibling subtask can wire the status string without an additional UI
PR.
Plumbing:
- @multica/core/types gains LocalDirectoryResourceRef +
UpdateProjectResourceRequest, and the api client gets the matching
PUT method backed by the server endpoint that landed in
|
||
|
|
25182995c6 |
fix(projects): accept SSH repo URLs for github_repo resources MUL-2112 (#2492)
* fix(projects): accept SSH repo URLs for github_repo resources (#2484) The project resource validator rejected anything that wasn't http(s), so workspace repos configured with an SSH remote (ssh:// or the scp-like `git@host:owner/repo.git` shorthand) could not be attached to a project. Both forms are valid git remotes and the daemon hands the URL straight to `git clone`, so the API has no reason to require https specifically. Relax the validator to accept http/https/ssh/git schemes and the scp-like shorthand, while still rejecting pasted garbage (no scheme, missing host, missing path, ftp://, file://, etc.). Co-authored-by: multica-agent <github@multica.ai> * fix(projects): reject scp-like URLs with '@' after ':' to avoid panic isValidGitRepoURL indexed '@' and ':' independently, then sliced s[at+1 : colon]. For inputs without '://' where '@' appears after the first ':' (e.g. `host:org/repo@branch`), `at+1 > colon` triggered a slice-bounds panic instead of a 400. Guard the slice and treat such inputs as malformed. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
44608713bb |
feat(projects): typed project resources + agent runtime injection (#1926)
* feat(projects): typed project resources + agent runtime injection
Adds a `project_resource` table that lets a project carry typed pointers
(github_repo today, more later) and surfaces them at agent runtime.
Server
- migration 065: project_resource (resource_type TEXT + resource_ref JSONB)
- sqlc CRUD + handler at /api/projects/{id}/resources
- claim handler attaches project_id/title + resources to issue tasks
Daemon
- TaskContextForEnv carries project context
- writes .multica/project/resources.json into workdir
- adds "## Project Context" block to CLAUDE.md / AGENTS.md / GEMINI.md
via type-dispatched formatter so new resource types just add a case
CLI
- multica project create --repo <url> attaches repos in one step
- multica project resource add/list/remove
Frontend
- Project create modal: Repos pill (workspace repos + ad-hoc URL)
- Project detail sidebar: collapsible Resources section with attach/remove
Docs
- New "Project Resources" chapter explaining the abstraction and
exactly what code to touch when adding a new resource type
Co-authored-by: multica-agent <github@multica.ai>
* fix(projects): transactional resources[] on create + generic CLI ref + test fix
Addresses review feedback on PR #1926:
1. CI red: TestProjectResourceLifecycle delete step called withURLParam
twice, which replaced the chi route context and dropped the project id.
Switched to the existing withURLParams helper from daemon_test.go.
2. POST /api/projects now accepts resources[] and attaches them in the
same transaction as the project. Invalid refs roll back the whole
create — no more half-attached projects on failure. Web modal + CLI
`project create --repo` both use the new bundled payload.
3. CLI `project resource add` now accepts a generic --ref '<json>' flag
so a new resource_type works without a CLI change. Per-type
shortcuts (--url for github_repo) remain as a convenience but are no
longer the only way in. Docs updated to drop the CLI from the
"files you must touch" list.
Adds two new server handler tests:
- TestCreateProjectAttachesResources (resources[] happy path)
- TestCreateProjectRollsBackOnInvalidResource (transactional rollback)
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|