Skip to content

🤖 feat: exec sub-agent git patch handoff#1965

Merged
ThomasK33 merged 21 commits intomainfrom
feat/subagent-git-patch-handoff
Jan 29, 2026
Merged

🤖 feat: exec sub-agent git patch handoff#1965
ThomasK33 merged 21 commits intomainfrom
feat/subagent-git-patch-handoff

Conversation

@ThomasK33
Copy link
Member

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.

📋 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:

  • 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:

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.

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-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.

Generated with mux • Model: openai:gpt-5.2 • Thinking: high • Cost: $9.01

@github-actions github-actions bot added the enhancement New feature or functionality label Jan 27, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@ThomasK33 ThomasK33 force-pushed the feat/subagent-git-patch-handoff branch from 03c7a43 to 02d9bd6 Compare January 28, 2026 21:32
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Breezy!

ℹ️ 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".

@ThomasK33 ThomasK33 force-pushed the feat/subagent-git-patch-handoff branch 2 times, most recently from 0963505 to 2535b72 Compare January 29, 2026 12:09
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@ThomasK33
Copy link
Member Author

@codex review

Addressed feedback: start git-format-patch artifact creation before resolving waiters so task_await can return a pending/ready artifact immediately after agent_report.

@chatgpt-codex-connector

This comment was marked as resolved.

@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ 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".

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>
@ThomasK33 ThomasK33 force-pushed the feat/subagent-git-patch-handoff branch from 65b9f6d to 3cb0407 Compare January 29, 2026 13:33
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>
@ThomasK33
Copy link
Member Author

@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>
@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ 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".

@ThomasK33 ThomasK33 added this pull request to the merge queue Jan 29, 2026
@ThomasK33 ThomasK33 removed this pull request from the merge queue due to a manual request Jan 29, 2026
@ThomasK33 ThomasK33 merged commit f5f5c35 into main Jan 29, 2026
38 of 40 checks passed
@ThomasK33 ThomasK33 deleted the feat/subagent-git-patch-handoff branch January 29, 2026 15:33
ethanndickson pushed a commit that referenced this pull request Jan 29, 2026
## 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant