Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4debe4c
feat(workflows): run Ralph stages from optional worktree
lavaman131 May 26, 2026
99e001c
fix(workflows): harden Ralph worktree lifecycle
lavaman131 May 26, 2026
a7d8ef1
fix(workflows): preserve Ralph worktrees on failure
lavaman131 May 26, 2026
1a9ba04
test(workflows): make Ralph worktree checks portable
lavaman131 May 26, 2026
1fd3801
fix(workflows): require git repo for Ralph worktrees
lavaman131 May 26, 2026
270455a
fix(workflows): preserve Ralph result on cleanup failure
lavaman131 May 26, 2026
afe9222
fix(workflows): preserve unpushed Ralph worktrees
lavaman131 May 26, 2026
ac13644
fix(workflows): harden Ralph worktree cleanup markers
lavaman131 May 26, 2026
576f59b
test(workflows): canonicalize Ralph worktree paths
lavaman131 May 26, 2026
3b38b4b
fix(workflows): reuse Ralph worktrees without cleanup
lavaman131 May 26, 2026
ddca6cd
test(workflows): escape null-byte fixture
lavaman131 May 26, 2026
dc7f706
fix(workflows): lock Ralph worktree runs
lavaman131 May 26, 2026
8cbc23f
revert(workflows): remove Ralph worktree lock
lavaman131 May 26, 2026
6cb0971
fix(workflows): validate empty graphs at runtime
lavaman131 May 26, 2026
8d2b5e1
fix(workflows): reject foreign Ralph worktrees
lavaman131 May 27, 2026
3b9e0ba
docs(workflows): clarify Ralph worktree sharing
lavaman131 May 27, 2026
d52a506
test(workflows): remove cwd-global test coupling
lavaman131 May 27, 2026
1b9a68a
fix(workflows): validate empty workflow graphs at startup
lavaman131 May 27, 2026
fe7b913
fix(workflows): clarify validation and worktree diagnostics
lavaman131 May 27, 2026
4ebf1e5
fix(workflows): revise Ralph specs in place
lavaman131 May 27, 2026
78c0927
fix(workflows): thread named workflow cwd
lavaman131 May 27, 2026
14ffc6c
fix(workflows): fail closed on Ralph path canonicalization
lavaman131 May 27, 2026
99fc7f3
fix(workflows): keep discovery side-effect free
lavaman131 May 27, 2026
4cb751c
test(workflows): avoid generated path interpolation
lavaman131 May 27, 2026
26f15d4
feat(workflows): add reusable git worktree bindings
lavaman131 May 27, 2026
79d6c2d
test(workflows): normalize worktree temp paths
lavaman131 May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/coding-agent/docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Atomic ships with four workflows you can run immediately. Use `/workflow list` t

<p align="center"><img src="images/workflow-list.png" alt="Workflow List" width="600" /></p>

Inputs are bare `key=value` tokens. Values are JSON-parsed when possible, so `count=5`, `flag=true`, and `objective="multi word value"` preserve useful types. If you call `/workflow <name>` without required inputs, the TUI opens an inline picker; pass `--no-picker` to skip it.
Inputs are bare `key=value` tokens. Values are JSON-parsed when possible, so `count=5`, `flag=true`, and `objective="multi word value"` preserve useful types. Some workflows expose reusable worktree inputs; for example, add `git_worktree_dir=../atomic-ralph-wt` to `ralph` to run its stages in a created/reused Git worktree while preserving your current repo-relative cwd. If you call `/workflow <name>` without required inputs, the TUI opens an inline picker; pass `--no-picker` to skip it.

You can also launch workflows with **natural language** — just describe the task in chat and ask Atomic to run the matching workflow:

Expand Down
30 changes: 27 additions & 3 deletions packages/coding-agent/docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,17 +237,21 @@ Inputs:
|---|---|---|---|---|
| `prompt` | text | yes | — | Task, feature request, issue summary, or spec path to plan, execute, refine, review, and prepare for PR. |
| `max_loops` | number | no | `10` | Maximum plan/orchestrate/review iterations before the workflow proceeds to PR handoff without reviewer approval. |
| `base_branch` | string | no | `origin/main` | Branch reviewers and the PR-prep stage compare the current code delta against. |
| `base_branch` | string | no | `origin/main` | Branch reviewers and the PR-prep stage compare the current code delta against; also used to create a missing worktree. |
| `git_worktree_dir` | string | no | `""` | Optional reusable Git worktree root. Empty runs in the invoking checkout; non-empty values run Ralph stages in the created/reused worktree. |

Run examples:

```text
/workflow ralph prompt="Plan and migrate the database layer to Drizzle" max_loops=3 base_branch=develop
/workflow ralph prompt="Refactor authentication across the API, CLI, and web UI, then prepare the PR"
/workflow ralph prompt="Safely implement the API refactor" git_worktree_dir=../atomic-ralph-api-wt base_branch=main
```

Each `ralph` iteration writes an RFC-style technical design document under `specs/`, initializes an OS-temp implementation notes file, delegates implementation through sub-agents, runs a behavior-preserving code simplifier, discovers review infrastructure, and asks two reviewers to inspect the patch against `base_branch`. The loop stops when every reviewer approves or `max_loops` is reached, then runs a pull-request preparation stage.

Set `git_worktree_dir` when you want Ralph's worker stages isolated in a reusable Git worktree. Relative paths resolve from the invoking repository root, existing same-repository worktree roots are reused, and missing paths are created from `base_branch`. Ralph preserves the invoking repo-relative cwd inside the worktree, so launching from `repo/packages/api` with `git_worktree_dir=../repo-wt` runs stages from `../repo-wt/packages/api`.

Result fields:

| Field | Meaning |
Expand Down Expand Up @@ -663,7 +667,7 @@ workflow({
})
```

Direct mode supports top-level/default options and per-task options such as `context`, `forkFromSessionFile`, `model`, `fallbackModels`, `thinkingLevel`, `tools`, `noTools`, `customTools`, `mcp`, `output`, `outputMode`, `reads`, `worktree`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, and `agentDir`. Direct chains also support `chainName`, `chainDir`, and `failFast`.
Direct mode supports top-level/default options and per-task options such as `context`, `forkFromSessionFile`, `model`, `fallbackModels`, `thinkingLevel`, `tools`, `noTools`, `customTools`, `mcp`, `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, and `agentDir`. Direct chains also support `chainName`, `chainDir`, and `failFast`.

For large fan-outs, prefer `outputMode: "file-only"` so the parent result contains compact file references instead of full output. Treat intercom payloads from async direct runs as user-visible workflow output.

Expand Down Expand Up @@ -713,6 +717,7 @@ Builder basics:
- Workflow names normalize for lookup: trim, lowercase, convert whitespace/underscore to hyphen, remove other punctuation, and collapse hyphens.
- `.description(text)` sets the listing text.
- `.input(key, schema)` declares typed user inputs.
- `.worktreeFromInputs({ gitWorktreeDir, baseBranch })` optionally maps input names to workflow-wide reusable Git worktree defaults.
- `.run(async (ctx) => { ... })` defines the workflow body.
- `.compile()` returns the workflow definition for discovery.

Expand Down Expand Up @@ -773,9 +778,28 @@ Common task/stage options include:
- `context: "fresh" | "fork"`, `forkFromSessionFile`
- `model`, `fallbackModels`, `thinkingLevel`, `scopedModels`, `modelRegistry`
- `tools`, `noTools`, `customTools`, `mcp: { allow?: string[], deny?: string[] }`
- `output`, `outputMode`, `reads`, `worktree`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, `agentDir`
- `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, `agentDir`
- advanced host-supplied SDK seams: `authStorage`, `resourceLoader`, `sessionManager`, `settingsManager`, `sessionStartEvent`

`gitWorktreeDir` selects a reusable Git worktree root for `ctx.stage`, `ctx.task`, `ctx.chain`, and `ctx.parallel`. If the path is missing, Atomic creates it with `git worktree add --detach <path> <baseBranch>`; if it exists, it must be a same-repository worktree root. The default stage cwd becomes the matching cwd inside the worktree and preserves the invoking repo-relative subdirectory. Explicit `cwd` still wins; relative `cwd` values resolve from the worktree cwd, while absolute `cwd` values are used as provided. `gitWorktreeDir` is mutually exclusive with `worktree: true`: use `gitWorktreeDir` for named/reusable worktrees and `worktree: true` for temporary direct-mode worktrees that are cleaned up after the run.

To bind user inputs to a workflow-wide worktree default, use the builder method:

```ts
export default defineWorkflow("safe-implementation")
.input("task", { type: "text", required: true })
.input("git_worktree_dir", { type: "string", default: "" })
.input("base_branch", { type: "string", default: "origin/main" })
.worktreeFromInputs({ gitWorktreeDir: "git_worktree_dir", baseBranch: "base_branch" })
.run(async (ctx) => {
const result = await ctx.task("implement", { task: String(ctx.inputs.task) });
return { result: result.text };
})
.compile();
```

For lower-level integrations, `@bastani/workflows` also exports `setupGitWorktree({ gitWorktreeDir, baseBranch, cwd })`, returning `{ worktreeRoot, cwd, repositoryRoot, created }` with the same validation, symlink-preserving path handling, and cwd-preservation behavior used by workflow stages.

`fallbackModels` retries transient provider/model failures with the primary `model` first, then each fallback, then the current Atomic-selected model when available. It is for rate limits, quota/auth/provider outages, unavailable models, network timeouts, and 5xx errors — not workflow-code errors, tool failures, validation failures, or cancellations.

## Programmatic Usage
Expand Down
16 changes: 16 additions & 0 deletions packages/workflows/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

### Added

- Added Ralph `git_worktree_dir` support for running stages from an optional Git worktree, reusing/sharing existing worktrees from the invoking repository as-is and leaving worktrees in place for retries.

### Changed

- Replaced regex-based workflow discovery stage validation with runtime empty-graph validation based on actual stage creation while keeping discovery side-effect-free.
- Threaded named workflow invocation cwd into workflow run contexts so workflow-owned artifacts can use the explicit runner cwd.
- Split worker project-initialization preflight guidance from Goal receipt/reporting instructions.

### Fixed

- Avoided blank deep-research display paths when a displayed artifact path equals the workflow invocation directory.
- Distinguished Ralph same-repository worktree classification and canonicalization failures from definitely non-Git existing `git_worktree_dir` paths.
- Updated Ralph to revise a stable original spec file across planner iterations and clarified `git_worktree_dir` null-byte diagnostics.

## [0.8.17] - 2026-05-26

### Changed
Expand Down
65 changes: 62 additions & 3 deletions packages/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,64 @@ export default defineWorkflow("review-and-merge")
.compile();
```

### Reusable Git worktrees

Use `gitWorktreeDir` when a workflow should run stages in a reusable Git worktree instead of the invoking checkout. The executor creates the worktree if it is missing, reuses it when it already exists as a same-repository worktree root, and defaults the stage/task `cwd` to the matching path inside that worktree.

```typescript
import { defineWorkflow } from "@bastani/workflows";

export default defineWorkflow("safe-implementation")
.description("Run implementation stages in a reusable worktree.")
.input("task", { type: "text", required: true })
.input("worktree", { type: "string", default: "" })
.input("base_branch", { type: "string", default: "origin/main" })
.worktreeFromInputs({
gitWorktreeDir: "worktree",
baseBranch: "base_branch",
})
.run(async (ctx) => {
const result = await ctx.task("implement", {
task: String(ctx.inputs.task),
// No cwd needed: when `worktree` is non-empty, this task runs from the
// corresponding cwd inside that reusable Git worktree.
});
return { result: result.text };
})
.compile();
```

You can also pass worktree options per stage/task or as shared chain/parallel defaults:

```typescript
await ctx.stage("review", {
gitWorktreeDir: "../review-worktree",
baseBranch: "origin/main",
}).prompt("Review the current changes.");

await ctx.parallel([
{ name: "security", task: "Security review" },
{ name: "runtime", task: "Runtime review" },
], {
gitWorktreeDir: "../review-worktree",
baseBranch: "origin/main",
failFast: false,
});
```

Worktree semantics:

- `gitWorktreeDir` must be used from inside a Git repository. Relative paths resolve from the logical invoking repository root; absolute paths are used as-is.
- If the requested path exists, it must be an actual Git worktree/checkout root belonging to the invoking repository. Existing subdirectories are rejected so writes do not silently land in the main checkout.
- If the path is missing, the parent directory is created and Git runs `git worktree add --detach <path> <baseBranch>`. `baseBranch` defaults to `HEAD` when omitted.
- The default execution cwd preserves the caller's repo-relative cwd inside the worktree. For example, invoking a workflow from `repo/packages/api` with `gitWorktreeDir=../repo-wt` runs stages from `../repo-wt/packages/api`.
- Symlinked repo/worktree paths preserve their logical spelling in the default cwd, matching Codex-style worktree behavior.
- Explicit `cwd` still wins. Relative `cwd` values are resolved against the worktree default cwd; absolute `cwd` values are used as provided.

`worktree: true` is different: it creates temporary isolated worktrees for direct task/parallel/chain execution and cleans them up afterward. It is mutually exclusive with `gitWorktreeDir`, which is intended for named/reusable worktrees that remain available across retries.

For advanced integrations, the SDK also exports `setupGitWorktree(options)`, which returns `{ worktreeRoot, cwd, repositoryRoot, created }` and uses the same validation/path behavior as the executor.

### Model fallbacks

Stages and high-level task helpers can retry transient provider/model failures with an ordered `fallbackModels` list. The primary `model` is tried first, then each fallback, and finally the current pi-selected model when available. Fallbacks are only used for retryable model/provider failures such as rate limits, quota/auth/provider outages, unavailable models, network timeouts, and 5xx errors — ordinary tool, shell, validation, cancellation, and workflow-code failures are not retried.
Expand Down Expand Up @@ -280,7 +338,7 @@ await runWorkflow({
});
```

The programmatic definition object mirrors the workflow tool: named workflow runs, single-task runs, parallel `tasks`, and mixed `chain` runs accept the same direct options (`reads`, `output`, `outputMode`, `worktree`, `maxOutput`, `artifacts`, `concurrency`, `failFast`, and stage/session options such as `cwd`, `agentDir`, `model`, `tools`, `context`, and `sessionDir`). `chainDir` is chain-only: it provides the shared artifact directory for chain reads, outputs, and worktree diffs.
The programmatic definition object mirrors the workflow tool: named workflow runs, single-task runs, parallel `tasks`, and mixed `chain` runs accept the same direct options (`reads`, `output`, `outputMode`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `concurrency`, `failFast`, and stage/session options such as `cwd`, `agentDir`, `model`, `tools`, `context`, and `sessionDir`). `chainDir` is chain-only: it provides the shared artifact directory for chain reads, outputs, and worktree diffs.

Workflow stage sessions follow Atomic SDK directory defaults: `DefaultResourceLoader` is initialized with the project `cwd` and the Atomic default `~/.atomic/agent` directory, while legacy `.pi` paths remain readable where the SDK supports multiple config directories. A stage-supplied `agentDir` is treated as an explicit user override; a stage-supplied `resourceLoader` owns discovery, with `cwd`/`agentDir` left for session naming and tool path resolution.

Expand Down Expand Up @@ -333,8 +391,9 @@ Plan → orchestrate → simplify → discover → review → PR-handoff workflo
| Input | Type | Required | Default | Description |
| ------------- | -------- | -------- | ------------- | ------------------------------------------------------------- |
| `prompt` | `text` | ✓ | — | Task, feature request, issue summary, or spec path to plan, execute, refine, review, and prepare for PR. |
| `max_loops` | `number` | — | `10` | Maximum plan/orchestrate/review iterations before PR handoff. |
| `base_branch` | `string` | — | `origin/main` | Branch reviewers and PR-prep compare the current delta with. |
| `max_loops` | `number` | — | `10` | Maximum plan/orchestrate/review iterations before PR handoff. |
| `base_branch` | `string` | — | `origin/main` | Branch reviewers and PR-prep compare the current delta with; also used to create a missing worktree. |
| `git_worktree_dir` | `string` | — | `""` | Optional reusable Git worktree root. Empty runs in the invoking checkout; non-empty values run Ralph stages in the created/reused worktree. |

### `open-claude-design`

Expand Down
Loading
Loading