Files
multica/server/internal/handler/daemon.go
Bohan Jiang ae68799c64 feat(task): reuse workdir on manual retry, gate session on error class (MUL-4869) (#5525)
* feat(task): reuse workdir on manual retry, gate session on error class (MUL-4869)

A manual retry (execution-log "retry" button -> POST /api/issues/{id}/rerun)
previously set force_fresh_session=true unconditionally, so the daemon threw
away the failed run's workdir and started from an empty one. For a transient
failure (network blip, provider 5xx / rate-limit, runtime_offline, timeout)
that discards work the agent already did.

New contract: a retry ALWAYS reuses the source task's workdir when it still
exists on disk; only the agent SESSION is gated, on whether the source task's
failure poisoned the conversation.

- RerunIssue derives force_fresh_session from the source task's failure_reason
  via resumeUnsafeFailureReason instead of hardcoding true. Transient/cancelled
  failures resume the session; conversation-poisoning failures start fresh.
- The daemon claim handler resolves the retry's session/workdir precisely from
  rerun_of_task_id, not the most-recent (agent,issue) row that a parallel task
  could hijack. PriorWorkDir is returned whenever present; PriorSessionID only
  when resume-safe AND on the same runtime.
- force_fresh_session now gates only the session, never the workdir.
- Add agent_error.context_overflow to the resume-unsafe set (Go helper plus the
  GetLastTaskSession / GetLastChatTaskSession blacklists): resuming an overflowed
  context would immediately overflow again.

Objectively-unreusable cases (workdir GC'd, different runtime, failed before a
workdir was recorded) fall back to a fresh Prepare via the daemon's existing
execenv.Reuse / gateResumeToReusedWorkdir path, so no daemon change is needed.

Tests: RerunIssue force_fresh classification by failure_reason; claim-layer
workdir-always-reused plus session gating (same/different runtime, poisoned);
context_overflow classifier.

Co-authored-by: multica-agent <github@multica.ai>

* fix(task): make manual-retry workdir reuse rollback-safe and error-text aware (MUL-4869)

Addresses code review on #5525.

- Rollback safety: RerunIssue no longer writes force_fresh_session based on the
  source failure. Rerun rows are pinned to force_fresh_session=true again, so an
  OLD claim handler picked up mid rolling-deploy degrades to a clean start
  instead of falling back to the (agent, issue) most-recent lookup and resuming
  a *different* execution. The new claim handler ignores the flag for reruns and
  computes session reuse from the exact source task (rerun_of_task_id).

- Legacy poison defense: add shared service.ResumeUnsafeFailure(failureReason,
  errorText), which combines the failure_reason poison set with the same
  400/invalid_request_error raw-error-text guard GetLastTaskSession applies. The
  claim handler's exact-source path uses it, so legacy 'agent_error' /
  deploy-window rows carrying a 400 marker are no longer resumed.

- CI: TestRerunIssueTargetsSourceTaskAgent passes again (rerun rows stay
  force_fresh_session=true). Service test now asserts that rollback-safe
  invariant across failure classes; the claim test drives session gating from
  the source task and adds a legacy-400 case.

- Docs: tasks.{mdx,zh,ja,ko}.mdx and the GetLastTaskSession SQL comment now
  distinguish execution-log per-row retry (task_id: reuse workdir, conditional
  session) from CLI/API rerun (no task_id: fresh session + fresh workdir).

- Clarify cross-runtime workdir is best-effort: the daemon offers the source
  workdir regardless of runtime (a shared mount may resolve it) and only the
  per-cwd session is runtime-gated.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Bohan-J <bohan@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>
2026-07-16 16:38:18 +08:00

152 KiB