🤖 feat: exec sub-agent git patch handoff#1965
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 03c7a43030
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
03c7a43 to
02d9bd6
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 02d9bd64e7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 02d9bd64e7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
0963505 to
2535b72
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2535b7259c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed feedback: start git-format-patch artifact creation before resolving waiters so |
This comment was marked as resolved.
This comment was marked as resolved.
|
@codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Change-Id: Id7c341665dba75ecd01862b45ac2368539fb036d Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ie3b215d57f777dd78dad42cb13a1adb95a4d342f Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I093af14b9ae2d799736f2c82841290a569019a7b Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I7e5004b49922ea4348ad023c5ea4de3c2d689fc4 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ia0e9ea878ac026c6d07e7d7837083a221e51da08 Signed-off-by: Thomas Kosiewski <tk@coder.com>
- Respect proposePlanImplementReplacesChatHistory when starting orchestrator - Replace history with the plan (not an internal orchestration message) - Update orchestrator agent prompt to encourage parallel delegation - Add UI tests for Start Orchestrator behavior Change-Id: If50000ee66ac14b0433e851e72d712dd15646a7c Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: If140aced7905305000ab9a63430613aa4b7cd7ce Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ib95d6965662ec166042dbe2db6f6605e5e28592f Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ic8a48b99edfd3e0b23e8005400713f9b8f488c5e Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I04d924f56d0b7eaf85e035dbdaf286b811b30766 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I508e8931165a9a7bf8bce5c0041b10209d53575e Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ia5b2cf7061510b2eab7e11673bef7d04925209e8 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ida645cd786313d8e8b283e853ed9f6a6b7998211 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I05f553d097f8c0db9b2f46dbb4566d56f4a8ae27 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: If9e8ae46eb44464ebbd4afb1f492f2c78d99fc0d Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ibf4e22d6d3a9a09cced3228ea4819c9b7bfb2bd3 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I11a942faf522b4153c50a050c3c0a8b16920d607 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I48cf838a4b1e5b7a6780d2a94f01e157ada6705e Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I92c38a671b886d4933b33c55fa5c7bb45e4b58e5 Signed-off-by: Thomas Kosiewski <tk@coder.com>
65b9f6d to
3cb0407
Compare
Add a dedicated UI renderer for task_apply_git_patch so patch application results are readable at a glance (commit count, HEAD SHA, dry-run), with actionable error/note details on expand. Also adds a Storybook story to exercise pending/success/failure states for Chromatic, plus a mockFactory helper. Signed-off-by: Thomas Kosiewski <tk@coder.com> --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.2` • Thinking: `xhigh`_ Change-Id: I78294b05a2ba25047959dab3448b3a2fe98bca2f Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
Use a human-friendly header label for the task_apply_git_patch tool call to avoid showing the raw tool name in the UI. Signed-off-by: Thomas Kosiewski <tk@coder.com> --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.2` • Thinking: `xhigh`_ Change-Id: Ic46f9a8fbfe71bb4bf707239c2c78f7e632bf0d7 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
Codex Review: Didn't find any major issues. Chef's kiss. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
## Summary
Adds a durable handoff mechanism for exec sub-agent work by exporting
child commits as a git `format-patch` artifact on `agent_report`, and
allowing the parent workspace to explicitly apply that patch via a new
`task_apply_git_patch` tool.
## Implementation
- Persist a stable `taskBaseCommitSha` for each child task at creation
time to generate deterministic patch ranges.
- Generate `git format-patch --stdout --binary <base>..HEAD` into the
*parent* session dir and defer leaf cleanup until patch generation
completes.
- Extend `task_await` to surface `artifacts.gitFormatPatch` metadata
when a task completes.
- Add `task_apply_git_patch` tool that applies the stored patch via `git
am` (with optional 3-way merge and a safe dry-run implemented via a
temporary worktree).
## Validation
- `make static-check`
- Added unit/integration tests covering artifact store, patch apply
tool, and exec `agent_report` patch generation + cleanup deferral.
## Risks
- Touches task cleanup and `agent_report` finalization logic; guarded to
apply only for `exec` subagents.
---
<details>
<summary>📋 Implementation Plan</summary>
# Plan: exec sub-agent git commit handoff (format-patch → git am)
## Context / Why
You want the **parent workspace** to act as an orchestrator/manager:
- Spawn multiple **exec sub-agents** in parallel.
- Each exec sub-agent works in its own git worktree/branch and can
commit freely.
- When the task finishes, Mux automatically produces a **durable
git-format-patch artifact** for that task.
- The parent later **pulls/applies** that patch via an explicit
action/tool (using `git am`), so the parent can sequence work and handle
conflicts.
Key constraints from your answers:
- Integration method: **`git format-patch` + `git am`**
- Export: **automatic backend plumbing on `agent_report`**, but
**pulled/used by parent later**
- Transport: **written to disk as a persisted artifact** (child
shouldn’t need to know)
- Apply behavior: **manual** (explicit tool call), not auto-apply
---
## Evidence (repo facts)
- **`agent_report` is a terminal “I’m done” marker**:
- Tool: `src/node/services/tools/agent_report.ts`
- Backend wiring: `TaskService.handleAgentReport()` /
`finalizeAgentTaskReport()` in `src/node/services/taskService.ts`
- **Exec sub-agents run in dedicated git worktrees** named
`agent_{agentType}_{taskId}`:
- Forking/worktrees: `src/node/worktree/WorktreeManager.ts`
- Task spawning: `src/node/services/taskService.ts`
- **Workspace sessions are deleted on workspace removal**, so storing
artifacts in the *child* session dir is unsafe if leaf tasks are
auto-cleaned:
- `WorkspaceService.remove()` deletes `~/.mux/sessions/{workspaceId}`
recursively: `src/node/services/workspaceService.ts`
- Session helpers: `Config.getSessionDir()` in `src/node/config.ts`;
`SessionFileManager` in `src/node/utils/sessionFile.ts`
- **Tools + schemas are centralized**:
- Zod schemas: `src/common/utils/tools/toolDefinitions.ts`
- Tool factories: `src/common/utils/tools/tools.ts` +
`src/node/services/tools/*`
---
## Recommended approach (net +~450–650 LoC product code)
### 1) Capture a stable “base commit” at task spawn (so patches are
reproducible)
**Problem:** `taskTrunkBranch` is a branch name; if the parent advances
while children run, we still want a deterministic range for “what this
task changed”.
**Plan:** Persist `taskBaseCommitSha` on the child task config entry at
creation.
- Where:
- `TaskService.create()` (or the underlying
`WorktreeManager.forkWorkspace()` return shape)
- Config types for workspace metadata
- How:
- `git -C <parentWorkspacePath> rev-parse HEAD` at spawn time
- Persist alongside existing `taskTrunkBranch`
- Back-compat:
- If `taskBaseCommitSha` is missing (older tasks/config), compute `git
merge-base <taskTrunkBranch> HEAD` during patch generation.
### 2) Define a durable “task patch artifact” stored under the *parent*
session dir
**Goal:** Patch survives child workspace/session deletion.
- Storage location (host filesystem):
- `path.join(config.getSessionDir(parentWorkspaceId),
"subagent-patches", childTaskId, "series.mbox")`
- Metadata:
- `subagent-patches.json` (or per-task JSON files) under the parent
session dir
- Use `SessionFileManager` + `workspaceFileLocks` for atomic updates
Suggested metadata shape:
```ts
type SubagentGitPatchArtifact = {
childTaskId: string;
parentWorkspaceId: string;
createdAtMs: number;
status: "pending" | "ready" | "failed" | "skipped";
baseCommitSha: string;
headCommitSha?: string;
commitCount?: number;
mboxPath?: string;
error?: string;
};
```
### 3) Generate `git format-patch` automatically when an exec task
reports
**Trigger point:** `TaskService.handleAgentReport()` /
`finalizeAgentTaskReport()`.
**Behavior (best-effort, doesn’t block reporting):**
1. On `agent_report` for an **exec** task, create/update the artifact
metadata as `pending`.
2. Start a background job (per-task) to:
- Resolve range: `taskBaseCommitSha..HEAD` (fallback merge-base).
- If no commits in range → mark `skipped` (optionally also note if
working tree dirty).
- Else generate patch:
- Prefer streaming implementation to avoid holding patch text in memory:
- spawn `git format-patch --stdout --binary <base>..HEAD`
- pipe stdout → `series.mbox`
- Record `headCommitSha` and `commitCount`.
- Mark artifact `ready` or `failed`.
3. Defer leaf-task cleanup until patch job completes:
- Update `cleanupReportedLeafTask()` to *not* delete the child workspace
while artifact status is `pending`.
- After patch job transitions out of `pending`, run the existing
cleanup.
Defensive programming guardrails:
- Assert we can locate both parent + child workspace paths in config.
- Fail “softly”: patch generation failure must not prevent report
delivery.
- Idempotency: if artifact already `ready` for that taskId, do nothing.
### 4) Plumb artifact availability back to the parent (machine-readable)
Avoid “parse reportMarkdown to find a path”.
**Preferred:** extend `task_await` completed result to include artifact
metadata.
- Update `TaskAwaitToolResultSchema` to optionally include:
- `artifacts?: { gitFormatPatch?: SubagentGitPatchArtifact }`
- Update the task await implementation to attach the artifact record
when returning `{ status: "completed" ... }`.
*(Optional but helpful)*: also append a short human-readable line into
the synthetic `<mux_subagent_report>` message, e.g. “Patch: ready
(commitCount=3). Apply via task_apply_git_patch(taskId=...)”.
### 5) Add an explicit parent tool: apply a task’s patch via `git am`
Add a new tool (name TBD, e.g. `task_apply_git_patch`) implemented in
`src/node/services/tools/`.
- Inputs (schema):
- `taskId: string` (child task)
- `dryRun?: boolean` (runs `git am --dry-run`)
- `threeWay?: boolean` default true (`--3way`)
- `force?: boolean` (allow apply when metadata says “already applied”)
- Execution:
- Locate artifact in *current/parent* session metadata.
- Ensure patch `status === "ready"` and file exists.
- Ensure parent working tree is clean before apply (unless `force`).
- Run: `git am --3way <series.mbox>`
- Outputs (schema):
- success: applied commit count + new HEAD sha
- failure: structured conflict info + next-step hints (`git am
--abort/--continue`)
- (Optional) After successful apply:
- Mark artifact as `appliedAtMs` in metadata (so re-apply is prevented
by default)
- Keep patch file on disk unless explicitly cleaned up
### 6) Tests
- Unit: patch artifact store read/write + state transitions
(pending→ready/failed/skipped).
- Integration:
- Create a temp git repo, spawn a fake child worktree, create commits,
simulate `agent_report`, verify `series.mbox` exists under parent
session dir.
- Verify apply tool runs `git am` successfully.
- Verify cleanup deferral: child workspace isn’t removed until patch job
finishes.
---
<details>
<summary>Alternatives considered (not recommended, but
cheaper)</summary>
### A) Cherry-pick child branch directly (net +~100–200 LoC)
Because child worktrees share the same underlying repo, the parent could
`git cherry-pick` commit SHAs or `git merge --ff-only agent_exec_<id>`.
Pros:
- No patch artifacts, no streaming IO.
Cons:
- Harder to treat as a durable “artifact”; less portable if we later
support cross-runtime transfer.
- Parent needs to know refs/SHAs; more git plumbing in the orchestrator.
### B) Unified diff artifacts (`git diff` + `git apply`) (net +~250–350
LoC)
Works even if the child didn’t commit.
Cons:
- Loses commit metadata, and conflicts tend to be worse than `git am
--3way`.
</details>
</details>
---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`openai:gpt-5.2` • Thinking: `high` • Cost: `$9.01`_
<!-- mux-attribution: model=openai:gpt-5.2 thinking=high costs=9.01 -->
---------
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Summary
Adds a durable handoff mechanism for exec sub-agent work by exporting child commits as a git
format-patchartifact onagent_report, and allowing the parent workspace to explicitly apply that patch via a newtask_apply_git_patchtool.Implementation
taskBaseCommitShafor each child task at creation time to generate deterministic patch ranges.git format-patch --stdout --binary <base>..HEADinto the parent session dir and defer leaf cleanup until patch generation completes.task_awaitto surfaceartifacts.gitFormatPatchmetadata when a task completes.task_apply_git_patchtool that applies the stored patch viagit am(with optional 3-way merge and a safe dry-run implemented via a temporary worktree).Validation
make static-checkagent_reportpatch generation + cleanup deferral.Risks
agent_reportfinalization logic; guarded to apply only forexecsubagents.📋 Implementation Plan
Plan: exec sub-agent git commit handoff (format-patch → git am)
Context / Why
You want the parent workspace to act as an orchestrator/manager:
git am), so the parent can sequence work and handle conflicts.Key constraints from your answers:
git format-patch+git amagent_report, but pulled/used by parent laterEvidence (repo facts)
agent_reportis a terminal “I’m done” marker:src/node/services/tools/agent_report.tsTaskService.handleAgentReport()/finalizeAgentTaskReport()insrc/node/services/taskService.tsagent_{agentType}_{taskId}:src/node/worktree/WorktreeManager.tssrc/node/services/taskService.tsWorkspaceService.remove()deletes~/.mux/sessions/{workspaceId}recursively:src/node/services/workspaceService.tsConfig.getSessionDir()insrc/node/config.ts;SessionFileManagerinsrc/node/utils/sessionFile.tssrc/common/utils/tools/toolDefinitions.tssrc/common/utils/tools/tools.ts+src/node/services/tools/*Recommended approach (net +~450–650 LoC product code)
1) Capture a stable “base commit” at task spawn (so patches are reproducible)
Problem:
taskTrunkBranchis a branch name; if the parent advances while children run, we still want a deterministic range for “what this task changed”.Plan: Persist
taskBaseCommitShaon the child task config entry at creation.TaskService.create()(or the underlyingWorktreeManager.forkWorkspace()return shape)git -C <parentWorkspacePath> rev-parse HEADat spawn timetaskTrunkBranchtaskBaseCommitShais missing (older tasks/config), computegit merge-base <taskTrunkBranch> HEADduring patch generation.2) Define a durable “task patch artifact” stored under the parent session dir
Goal: Patch survives child workspace/session deletion.
path.join(config.getSessionDir(parentWorkspaceId), "subagent-patches", childTaskId, "series.mbox")subagent-patches.json(or per-task JSON files) under the parent session dirSessionFileManager+workspaceFileLocksfor atomic updatesSuggested metadata shape:
3) Generate
git format-patchautomatically when an exec task reportsTrigger point:
TaskService.handleAgentReport()/finalizeAgentTaskReport().Behavior (best-effort, doesn’t block reporting):
agent_reportfor an exec task, create/update the artifact metadata aspending.taskBaseCommitSha..HEAD(fallback merge-base).skipped(optionally also note if working tree dirty).git format-patch --stdout --binary <base>..HEADseries.mboxheadCommitShaandcommitCount.readyorfailed.cleanupReportedLeafTask()to not delete the child workspace while artifact status ispending.pending, run the existing cleanup.Defensive programming guardrails:
readyfor that taskId, do nothing.4) Plumb artifact availability back to the parent (machine-readable)
Avoid “parse reportMarkdown to find a path”.
Preferred: extend
task_awaitcompleted result to include artifact metadata.TaskAwaitToolResultSchemato optionally include:artifacts?: { gitFormatPatch?: SubagentGitPatchArtifact }{ status: "completed" ... }.(Optional but helpful): also append a short human-readable line into the synthetic
<mux_subagent_report>message, e.g. “Patch: ready (commitCount=3). Apply via task_apply_git_patch(taskId=...)”.5) Add an explicit parent tool: apply a task’s patch via
git amAdd a new tool (name TBD, e.g.
task_apply_git_patch) implemented insrc/node/services/tools/.taskId: string(child task)dryRun?: boolean(runsgit am --dry-run)threeWay?: booleandefault true (--3way)force?: boolean(allow apply when metadata says “already applied”)status === "ready"and file exists.force).git am --3way <series.mbox>git am --abort/--continue)appliedAtMsin metadata (so re-apply is prevented by default)6) Tests
agent_report, verifyseries.mboxexists under parent session dir.git amsuccessfully.Alternatives considered (not recommended, but cheaper)
A) Cherry-pick child branch directly (net +~100–200 LoC)
Because child worktrees share the same underlying repo, the parent could
git cherry-pickcommit SHAs orgit merge --ff-only agent_exec_<id>.Pros:
Cons:
B) Unified diff artifacts (
git diff+git apply) (net +~250–350 LoC)Works even if the child didn’t commit.
Cons:
git am --3way.Generated with
mux• Model:openai:gpt-5.2• Thinking:high• Cost:$9.01