Skip to content

Add workspaces - #68

Open
adampoit wants to merge 1 commit into
mainfrom
workspaces
Open

Add workspaces#68
adampoit wants to merge 1 commit into
mainfrom
workspaces

Conversation

@adampoit

@adampoit adampoit commented Aug 5, 2026

Copy link
Copy Markdown
Owner

No description provided.

@not-adam

not-adam Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Mira PR Walkthrough

This PR introduces "composed workspaces" to Patchlane, enabling agents and developers to work against a complete composed fork (all lanes assembled) while committing to a single target lane. It adds a workspace CLI command with create, status, land, and remove subcommands, along with core modules for composition planning, workspace state management, Git worktree lifecycle, and landing validation (including round-trip tree comparison). The package version bumps to 0.5.3 while keeping the config schema at version 1.

graph LR
    cli["src/cli.ts"]
    wcreate["src/workspace-create.ts"]
    wstatus["src/workspace-status.ts"]
    wland["src/workspace-land.ts"]
    wremove["src/workspace-remove.ts"]
    composition["src/composition.ts"]
    wstate["src/workspace-state.ts"]
    git["src/git.ts"]
    config["src/config.ts"]
    errors["src/composition-errors.ts"]

    cli --> wcreate
    cli --> wstatus
    cli --> wland
    cli --> wremove
    wcreate --> composition
    wcreate --> wstate
    wcreate --> git
    wcreate --> config
    wstatus --> wstate
    wstatus --> git
    wland --> composition
    wland --> wstate
    wland --> git
    wland --> wstatus
    wremove --> wstate
    wremove --> wstatus
    wremove --> git
    composition --> git
    composition --> errors
    composition --> config
Loading
Confidence: 4/5   ◉◉◉◉○   Safe with minor fixes
  • All new code in well-factored modules with no modifications to existing core paths. Minor risk from version bump to 0.5.3 and the absence of new test files alongside ~1,600 new lines. The code follows existing patterns (error classes, Git abstraction, CLI structure) consistently.

Key files to review:

  • src/cli.ts:170 — CLI options --origin-remote-name, --upstream-remote-name, and UPSTREAM_REMOTE_URL are not propagated from the CLI workspace command handler to landWorkspace(), making the advertised CLI flags silently ineffective for the land action.
  • src/workspace-create.ts:118 — No tests added for ~1,700 lines of new workspace logic including commit replay, exact tree comparison, atomic state persistence, and cleanup error paths.

Blast Radius — 2 dependent repositories, 3 total references

adampoit/mira — 1 reference
adampoit/wezterm — 2 references

24 files reviewed · 2 comments (⚠️ 2 warnings)


Comment @not-adam help to get the list of available commands and usage tips.

@not-adam not-adam Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Mira Review Summary

This is a large, well-structured PR implementing composed workspaces for Patchlane 0.5.3. The architecture is sound: separating composition from sync, pinning lane SHAs at workspace creation time, and enforcing exact tree equality via round-trip validation. Two issues need attention before merge: (1) the CLI workspace land handler does not propagate --origin-remote-name, --upstream-remote-name, or UPSTREAM_REMOTE_URL options to landWorkspace(), making those CLI flags silently ignored; (2) no tests were added alongside the ~1,700 lines of new source code — the workspace creation, landing (including projection, recomposition, and exact tree comparison), state persistence, and cleanup paths all need coverage.

Key Issues

Issue Location
🔴 CLI options --origin-remote-name, --upstream-remote-name, and UPSTREAM_REMOTE_URL are not propagated from the CLI workspace command handler to landWorkspace(), making the advertised CLI flags silently ineffective for the land action. src/cli.ts:170
🔴 No tests added for ~1,700 lines of new workspace logic including commit replay, exact tree comparison, atomic state persistence, and cleanup error paths. src/workspace-create.ts:118

Comment thread src/workspace-create.ts
Comment on lines +118 to +151
try {
git(['worktree', 'add', '--detach', destination, plan.source.sha], cwd);
worktreeCreated = true;
const composition = composeIntoWorktree(plan, { cwd: destination });
git(['branch', branch, composition.headSha], cwd);
branchCreated = true;
git(['switch', branch], destination);
git(['config', '--local', `branch.${branch}.patchlane-workspace`, id], cwd);

const state: WorkspaceState = {
version: 1,
id,
path: destination,
branch,
createdAt: new Date().toISOString(),
configRef,
originRemoteName,
upstreamRemoteName,
source: plan.source,
targetLane,
baselineCommit: composition.headSha,
baselineTree: composition.treeSha,
laneOrder: plan.lanes.map((lane) => lane.ref),
laneTips: Object.fromEntries(plan.lanes.map((lane) => [lane.ref, lane.tipSha])),
laneDiffBases: Object.fromEntries(plan.lanes.map((lane) => [lane.ref, lane.diffBaseSha])),
landedLaneSha: null,
};
// Validate against the registered worktree before atomically publishing state.
parseWorkspaceState(state, { cwd, requireRegisteredWorktree: true });
writeWorkspaceState(state, cwd);
return { state, plan };
} catch (error) {
if (worktreeCreated || branchCreated) cleanUpWorkspace(cwd, destination, branch, id);
throw error;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Refactor suggestion
⚠️ Warning

Missing test coverage

This PR adds ~1,700 lines of new source code across 7 new files (src/composition.ts, src/composition-errors.ts, src/git.ts, src/workspace-state.ts, src/workspace-create.ts, src/workspace-status.ts, src/workspace-land.ts, src/workspace-remove.ts) with non-trivial logic including Git worktree management, commit replay, exact tree comparison, lease-based ref updates, and atomic state persistence. The PR contains no corresponding test additions. Critical untested paths include the lane projection and exact-tree-comparison round trip in landWorkspace, the freshness validation in validateLaneFreshness, the atomic state write/read cycle in writeWorkspaceState/parseWorkspaceState, and the cleanup path in createWorkspace when worktree creation succeeds but state registration fails.

Not useful? Reply @not-adam reject to dismiss this suggestion.

Comment thread src/cli.ts
Comment on lines +170 to +177
if (action === 'land') {
const result = landWorkspace({
lane: args.lane,
dryRun: args.dryRun === true,
push: args.push === true,
});
process.stdout.write(`${args.json ? formatWorkspaceLandJson(result) : formatWorkspaceLand(result)}\n`);
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug
⚠️ Warning

CLI options not propagated to workspace land

The land action handler at line 170-177 passes only lane, dryRun, and push to landWorkspace(). It does not propagate --origin-remote-name, --upstream-remote-name, or UPSTREAM_REMOTE_URL (which create does propagate at lines 148-157). While landWorkspace defaults originRemoteName and upstreamRemoteName from workspace state (lines 333-334 of workspace-land.ts) and upstreamRemoteUrl from process.env, this means the CLI flags are silently ignored for land. Users who need to override these values for the land operation (e.g., when the remote name has changed since workspace creation) will get unexpected behavior with no error.

Suggested change
if (action === 'land') {
const result = landWorkspace({
lane: args.lane,
dryRun: args.dryRun === true,
push: args.push === true,
});
process.stdout.write(`${args.json ? formatWorkspaceLandJson(result) : formatWorkspaceLand(result)}\n`);
return;
if (action === 'land') {
const result = landWorkspace({
lane: args.lane,
dryRun: args.dryRun === true,
push: args.push === true,
originRemoteName: args.originRemoteName,
upstreamRemoteName: args.upstreamRemoteName,
upstreamRemoteUrl: env('UPSTREAM_REMOTE_URL'),
});
process.stdout.write(`${args.json ? formatWorkspaceLandJson(result) : formatWorkspaceLand(result)}\n`);
return;
}

Prompt for AI Agents
In src/cli.ts, in the workspace command action handler under the `action === 'land'` branch (around line 170), add `originRemoteName: args.originRemoteName`, `upstreamRemoteName: args.upstreamRemoteName`, and `upstreamRemoteUrl: env('UPSTREAM_REMOTE_URL')` to the `landWorkspace()` call options object so the CLI flags are propagated to the land function, consistent with how they are passed in the `create` action handler.

Apply this code change:

			if (action === 'land') {
				const result = landWorkspace({
					lane: args.lane,
					dryRun: args.dryRun === true,
					push: args.push === true,
					originRemoteName: args.originRemoteName,
					upstreamRemoteName: args.upstreamRemoteName,
					upstreamRemoteUrl: env('UPSTREAM_REMOTE_URL'),
				});
				process.stdout.write(`${args.json ? formatWorkspaceLandJson(result) : formatWorkspaceLand(result)}\n`);
				return;
			}

Not useful? Reply @not-adam reject to dismiss this suggestion.

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