mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
Phase 0 hotfix for the cross-workspace contamination reported in MUL-1027 / #1235: an agent running for workspace A ended up commenting on (and renaming) a two-day-old issue in workspace B. #1249/#1259 fixed resolution for autopilot tasks and consolidated the task-workspace resolver, and #1294 populated workspace_id in the claim response for run_only autopilot tasks. Those closed the known fallthroughs but the failure mode is still broader: whenever the daemon or server fails to supply a workspace, the CLI silently falls back to `~/.multica/config.json`, which is user-global, not workspace-scoped. On a host running daemons for multiple workspaces, a single gap in workspace propagation is enough to leak writes across workspaces. This PR adds three coordinated guards so no single layer's bug can cause a cross-workspace write: 1. `server/cmd/multica/cmd_agent.go` — `resolveWorkspaceID` detects the agent execution context (`MULTICA_AGENT_ID` / `MULTICA_TASK_ID` env, both daemon-only markers) and in that context refuses to fall back to the user-global CLI config. Human / script usage (no agent env) is unchanged: flag → env → config fallback chain still applies. 2. `server/internal/handler/daemon.go` — `ClaimTaskByRuntime` now captures the runtime's workspace from `requireDaemonRuntimeAccess` and enforces `resolved_task_workspace == runtime_workspace` after the existing issue/chat/autopilot branches. On mismatch or empty, the handler explicitly cancels the just-dispatched task (via `TaskService.CancelTask`, which also reconciles agent status) and returns 500. Without the explicit cancel, `ClaimTaskForRuntime` had already transitioned the task to 'dispatched' and the agent status to 'working', so a plain 500 would leave both stuck for the ~5 min stale-task sweep window. 3. `server/internal/daemon/daemon.go` — `runTask` refuses to spawn the agent when `task.WorkspaceID` is empty (defense-in-depth against server bugs and reused workdirs). Tests: - `cmd/multica/cmd_agent_test.go`: `TestResolveWorkspaceID_AgentContextSkipsConfig` — five subtests covering the full fallback matrix (outside agent context still reads config; agent context uses env; agent context with empty env returns empty; task-id-only marker also counts; requireWorkspaceID surfaces the agent-context error message). - `internal/handler/daemon_test.go`: `TestClaimTaskByRuntime_TaskWorkspaceMismatch_CancelsAndRejects` — constructs a data-inconsistent task (runtime_id in workspace A, issue_id in workspace B) and asserts the handler returns 500 AND leaves the task in 'cancelled' state (not 'dispatched'). Phase 1/2 follow-ups (prompt injection of workspace slug, session lookup workspace filter, cross-workspace audit of agent-facing endpoints, observability) are out of scope for this PR and tracked separately.