Skip to content

feat: fork a channel into a sibling channel - #470

Merged
iamnbutler merged 5 commits into
mainfrom
fork-channel
Oct 16, 2026
Merged

iamnbutler merged 5 commits into
mainfrom
fork-channel

Conversation

@iamnbutler

Copy link
Copy Markdown
Contributor

Part of #33. Adds Fork channel: a new sibling channel in the source's project, following the decision on #33 and old Ace's POST /channels/{cid}/fork.

  • Host: fork host op (owner only, from the host that runs the source; peers refused like create) and ace fork <channel>. manage.fork reads the source's info and root-chat changes, picks a name, runs lane new <fork>/<lane> --base <source HEAD> in the project, saves the record, and posts the seed message.
  • Channel: a new optional Options.lane, from the catalog record's new optional lane field. It is applied only through harness.root's init, in the commit that creates the root chat, which records the LanesDoc entry and sets the chat's cwd. Reopening never moves a chat. The pi format is unchanged (an existing LanesDoc entry shape), so no migration is needed.
  • App: wires the existing onFork / "Fork channel" item in packages/ui session-item actions. It shows a toast and selects the fork.
  • Docs: architecture (Channel section) and data.md (catalog record field).

Product decisions

  • Name is fork-of-<name> (as in old Ace). It gets -2, -3… past existing channel names and lane branch prefixes left in the project. It is recorded as not deliberate (named: false), so the fork's agent renames it once its work takes shape.
  • History is not copied. The seed is a non-invoking say from the person who forked. It names #source (channel <id>), includes the source's summary, and gives the lane and commit it continues from, with a note when uncommitted or untracked work stayed behind. No run starts and no credentials are needed.
  • The fork's lane keeps the source lane's name, starts at the source lane's HEAD, and uses the source's fork point as its Diff base. The fork's Diff shows the source's commits plus its own.
  • Model is the source root chat's current model, falling back to the source's initial model.
  • Cloud compute channels are refused for now, because their lanes live in the container. Teammates' channels are refused with "Fork channels from the host that runs them".

Remaining on #33: forking cloud compute channels; "Fork from this message" as a chat fork (after #19).

Smoke test (isolated ACE_HOME/ACE_CONFIG_HOME, real anthropic/claude-haiku-4-5):

  • A source channel's agent started lane try-fork, committed b.txt, left c.txt untracked and set a summary.
  • ace fork created fork-of-fork-src with worktree .lane/trees/fork-of-fork-src/try-fork at the source's commit. ace info showed the root chat on lane try-fork with that cwd. The transcript showed the seed message with summary and uncommitted-work note.
  • Invoking the fork's agent confirmed its cwd and log, and it committed on fork-of-fork-src/try-fork.
  • The source was unchanged: ace backup before and after gave identical sqlite3 .dump output, and the source lane's HEAD and status were unchanged.
  • Through ace serve --loopback, the gateway fork op created fork-of-fork-src-2 (numbering) and a fork of the fork (lane at the fork's new commit).
  • Not exercised: clicking the sidebar menu item in the app (to avoid driving the shared desktop while another agent works in the app), and hosted-channel sources.

Built in Ace

Built with Claude Code outside Ace, in a session already running there; no Ace gap involved.

@iamnbutler iamnbutler mentioned this pull request Oct 16, 2026
1 of 3 tasks
Fork channel (sidebar menu, `ace fork`, host op `fork`) creates a new channel in the
source's project, seeded with a message naming the source and carrying its summary. The
source root chat's lane continues as a new lane from its current commit; the source is
only read.

Why: packages/channel/src/channel.ts#class Channel | a fork's starting lane is recorded only in the commit that creates the root chat, so reopening never moves a chat that switched lanes
Why: apps/host/src/manage.ts#function fork | forking only reads the source channel: info and changes requests, never writes to its pi state or lane
@iamnbutler
iamnbutler merged commit 39b81dc into main Oct 16, 2026
1 check passed
@iamnbutler iamnbutler mentioned this pull request Oct 16, 2026
2 of 7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant