Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .github/workflows/template-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
- name: Worktree-helper install smoke (against a throwaway HOME)
run: ./scripts/install-smoke-assert.sh

- name: Worktree-resume smoke (local bare repo as origin)
run: ./scripts/worktree-resume-assert.sh

- name: Configure git identity for the scaffold commit
run: |
git config --global user.email "ci@example.com"
Expand Down
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ bash -n demo/setup.sh demo/teardown.sh demo/lib.sh
# Worktree-helper install: run `cdd-worktree.sh install` against a throwaway HOME.
./scripts/install-smoke-assert.sh

# Worktree-resume: recreate a worktree on an existing remote branch, using a
# local bare repo as `origin` and a stubbed `claude` (no real session launched).
./scripts/worktree-resume-assert.sh

# End-to-end smoke: bootstrap into a tmpdir and run the assertion script.
rm -rf /tmp/cdd-smoke && mkdir -p /tmp/cdd-smoke
./tools/bootstrap-cdd-project.sh --name "Demo Project" \
Expand Down Expand Up @@ -99,6 +103,7 @@ See `doc/knowledge_base/claude-driven-development.md` for the full picture.
This project uses CDD on itself. Every CDD session is a fresh context doing exactly one job (see process doc section 3 for the session taxonomy).

- **To start a new task** (handoff session): run `/cdd-next-step` from the main worktree to produce a handoff, then run `cdd-worktree <branch>` to spin up the implementation worktree (implementation session, opens in plan mode). `/cdd-next-step` has three front-ends: no argument picks the next roadmap item; a task prompt starts off-roadmap work (intent-driven); and `#NN` / a bare integer / the `issue`/`issues` keyword sources the task from a GitHub issue (issue-driven), naming the branch `gh_issue_NN_<slug>`.
- **To pick up a task started on another machine** (resume): run `cdd-worktree-resume [<branch>]` from the main worktree. It recreates the worktree on the existing remote branch (no handoff needed) and launches Claude Code so you can run `/cdd-process-pr`, `/cdd-merge-base`, or `/cdd-pre-pr`; with no argument it lists resumable remote branches.
- **When main has advanced under a feature branch** (merge session): run `/cdd-merge-base` in a fresh context on the feature branch.
- **Before opening a PR** (pre-PR session): run `/cdd-pre-pr` in a fresh context to verify the process doc and template are consistent and the roadmap reflects what landed; it auto-commits its own reconciliation edits (local, no push) and ends with an opt-in step to open the PR (adding `Closes #NN` when the branch carries the `gh_issue_NN` token).
- **When a PR review leaves comments** (PR-review session): run `/cdd-process-pr` in a fresh context on the feature branch.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ CDD ships seven slash commands, all prefixed `cdd-` so they autocomplete as a gr
| `/cdd‑retrofit` | Install or upgrade CDD in an existing project. |
| `/cdd‑quick‑create` | Produce a one-off self-contained deliverable (script + README), no project substrate. |

`cdd-worktree` (and its companion `cdd-worktree-done`) is a **shell helper**, not a slash command. It's a single project-independent script — a machine-global toolchain dependency, like `git` or `gh` — that you install once and that then works in every CDD project. From a CDD repo checkout: `tools/cdd-worktree.sh install`. On a fresh machine with only a downstream project (no CDD repo), one command fetches and installs it:
`cdd-worktree` (and its companions `cdd-worktree-done`, `cdd-worktree-list`, and `cdd-worktree-resume`) is a **shell helper**, not a slash command. It's a single project-independent script — a machine-global toolchain dependency, like `git` or `gh` — that you install once and that then works in every CDD project. From a CDD repo checkout: `tools/cdd-worktree.sh install`. On a fresh machine with only a downstream project (no CDD repo), one command fetches and installs it:

```bash
curl -fsSL https://raw.githubusercontent.com/drabaioli/cdd/main/tools/cdd-worktree.sh \
--create-dirs -o ~/.cdd/tools/cdd-worktree.sh \
&& bash ~/.cdd/tools/cdd-worktree.sh install
```

Either form wires `~/.bashrc` and `~/.zshrc` (idempotent); open a new shell afterwards. It spins up and tears down the per-task git worktree that an implementation session runs in.
Either form wires `~/.bashrc` and `~/.zshrc` (idempotent); open a new shell afterwards. It spins up and tears down the per-task git worktree that an implementation session runs in, and `cdd-worktree-resume [<branch>]` recreates that worktree on a second machine — tracking the existing remote branch, no handoff needed — so a task started elsewhere can be picked up to run `/cdd-process-pr`, `/cdd-merge-base`, or `/cdd-pre-pr`.

## Questions?

Expand Down
9 changes: 6 additions & 3 deletions doc/knowledge_base/claude-driven-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,14 @@ Slash commands are declarative: they describe what to do, not how to orchestrate

### 2.8 The worktree shell helper (`cdd-worktree`)

A single, project-independent bash helper provides three commands. It is the same script for every CDD project: the functions derive the repository name, default branch, and handoff directory at runtime, so there is no per-project copy.
A single, project-independent bash helper provides four commands. It is the same script for every CDD project: the functions derive the repository name, default branch, and handoff directory at runtime, so there is no per-project copy.

- `cdd-worktree <branch>`, creates a worktree for `<branch>` and launches Claude Code in plan mode in it with the suggested first prompt already submitted. Requires a handoff file to exist.
- `cdd-worktree-done`, run from a feature worktree once the PR has landed or the branch is being abandoned. Returns to the default branch, pulls, removes the worktree, resolves the branch (safe-delete if merged, force-delete if squash-merged, prompt otherwise), and deletes the handoff — and its sibling state record (§2.13) — iff the branch was deleted.
- `cdd-worktree-list`, lists active handoffs with worktree/branch/PR status. Highlights stale entries.
- `cdd-worktree-resume [<branch>]`, picks up a task started on another machine. It recreates a worktree tracking an **existing remote branch** (no handoff required) and launches plain Claude Code so you can run a resume-side command (`/cdd-process-pr`, `/cdd-merge-base`, `/cdd-pre-pr`). With no argument it lists remote feature branches not already checked out and prompts for one. See the multi-machine note below.

**Multi-machine pickup (`cdd-worktree-resume`).** A task can start on one machine — worktree and branch created, branch pushed, PR opened — and need to continue on a second machine that has only a clone of the repo: no local branch, no worktree, no handoff. `cdd-worktree-resume` fetches `origin`, recreates the worktree on the existing remote branch, and drops you into Claude Code to run the resume-side command of your choice. Its scope is deliberately **worktree and branch recreation only**: the handoff (§2.6) and the state record (§2.13) are local-per-machine artifacts and are *not* transferred. That is sound, because the resume-side commands read PR and branch state from git and `gh`, not from the handoff, and `cdd-state set` no-ops when the record is absent. Cross-machine *sync* of the handoff and state record is separate, still-future work (issue #22); the chosen direction is sketched in the roadmap.

The helper installs itself to a stable home that does not depend on a live CDD checkout. Run `tools/cdd-worktree.sh install` once (the script is dual-mode: sourced it defines the functions; run directly with `install` it sets itself up): this copies the script to `~/.cdd/tools/cdd-worktree.sh`, appends a marker-guarded source line to `~/.bashrc` and `~/.zshrc` (idempotent), and migrates any handoffs from the legacy `~/.claude-handoffs/` location. After installing, the commands work in every CDD project — including ones bootstrapped later — without any further per-project setup.

Expand All @@ -145,7 +148,7 @@ curl -fsSL https://raw.githubusercontent.com/drabaioli/cdd/main/tools/cdd-worktr
&& bash ~/.cdd/tools/cdd-worktree.sh install
```

The helper is a machine-global toolchain dependency, like `git` or `gh`: installed once per machine, newest wins, install idempotent. Install from latest `main`, never pinned per project — pinning would reintroduce the very conflict a single shared helper avoids. Its contract with projects is deliberately tiny and frozen: the three command names above plus the `~/.cdd/handoffs/<repo>/<branch>.md` layout; everything project-specific is derived at runtime, so one copy stays compatible with every project and there is by construction no per-project helper to conflict. When that state must evolve, the change ships as a one-shot migration inside `install` (the `~/.claude-handoffs/` → `~/.cdd/handoffs/` move is the example), re-homing every project at once.
The helper is a machine-global toolchain dependency, like `git` or `gh`: installed once per machine, newest wins, install idempotent. Install from latest `main`, never pinned per project — pinning would reintroduce the very conflict a single shared helper avoids. Its contract with projects is deliberately tiny: the command names above plus the `~/.cdd/handoffs/<repo>/<branch>.md` layout; everything project-specific is derived at runtime, so one copy stays compatible with every project and there is by construction no per-project helper to conflict. The contract grows only additively — `cdd-worktree-resume` was added without changing the layout or any existing command (it reads remote git state, not the handoff), so a newer helper stays backward-compatible with every project version. When that state must evolve, the change ships as a one-shot migration inside `install` (the `~/.claude-handoffs/` → `~/.cdd/handoffs/` move is the example), re-homing every project at once.

The helper derives the repository's default branch from `origin`'s HEAD (falling back to `main`) and assumes the remote is named `origin`; the remote-name assumption is documented in `template/BOOTSTRAP.md`.

Expand Down Expand Up @@ -209,7 +212,7 @@ A practice moves from **expected** to **enforced** in the same change that lands

A small JSON file recording where a task sits in its lifecycle and which Claude Code sessions have worked it. It is an **additive sibling of the handoff** (§2.6): same per-repo directory, same `<branch>` basename, same branch-scoped/ephemeral lifecycle. The primary payoff is the **session chain** — given a branch, you can find and resume the session(s) that worked it (`claude --resume <id>`) without grepping shell history. A dashboard (`cdd-dash`) is one downstream consumer, not the justification.

The record is **advisory**: a consumer that finds it missing or stale falls back to inferring state from handoffs, branches, and `gh`; a writer that finds it missing does not fabricate one (only `/cdd-next-step` creates it). It is a **local cache** of work on *this* machine — not a cross-machine transfer mechanism and not an event history; multi-machine resume is separate future work (issue #22). It fits the frozen worktree-helper contract (§2.8) without enlarging it: `cdd-worktree-done` only deletes the record, alongside the handoff.
The record is **advisory**: a consumer that finds it missing or stale falls back to inferring state from handoffs, branches, and `gh`; a writer that finds it missing does not fabricate one (only `/cdd-next-step` creates it). It is a **local cache** of work on *this* machine — not a cross-machine transfer mechanism and not an event history. Recreating a worktree on another machine is handled by `cdd-worktree-resume` (§2.8), which rebuilds the worktree from the remote branch without this record; *syncing* the record (and the handoff) across machines is separate future work (issue #22). It fits the frozen worktree-helper contract (§2.8) without enlarging it: `cdd-worktree-done` only deletes the record, alongside the handoff.

Writes go through a small **`cdd-state` helper** (`tools/cdd-state.sh`), dual-mode and self-installing like the worktree helper: `cdd-state seed <branch>` creates the record, `cdd-state set <stage> [--pr N]` advances it. Routing every write through one helper makes them atomic (temp-file + rename) and well-formed, so the malformed-JSON / wrong-field failure mode of hand-editing is gone; the model still decides *when* to call it. The helper is advisory end-to-end — absent `jq` or an absent record, it no-ops rather than failing the workflow.

Expand Down
3 changes: 2 additions & 1 deletion doc/knowledge_base/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Give each task a machine-readable record of where it sits in its lifecycle and w
- [x] Per-task state record + `cdd-state` helper: a `<branch>.state.json` sibling of the handoff, advanced through the lifecycle by the slash commands via `tools/cdd-state.sh` (atomic `seed`/`set`, self-installing). Advisory, local-only, append-only `{id, stage}` session chain. Full design and schema in process doc §2.13. — §2.13 + §2.6/§2.8/§3.3, all four command copies (repo + template), both `settings.json`, `tools/cdd-state.sh` (new) and `tools/cdd-worktree.sh` (deletion), architecture/feature docs, BOOTSTRAP.md.
- [ ] Harden the one outcome transition a tool call owns: a `PostToolUse` hook on `gh pr create` that parses the PR number and writes `pr_open`/`pr=NN` mechanically (`cdd-state` as the hook target), removing the model-remembering dependency. (A `UserPromptSubmit` hook fires deterministically on every `/cdd-*` call, but only at invocation — it can stamp "stage started", not outcomes like `checks_passed` or the PR number, which stay model-driven via `cdd-state set`.)
- [ ] Consume the record: teach the `cdd-dash` dashboard to read `stage`/`sessions` instead of inferring task state. (`cdd-worktree-list` already infers worktree/branch/PR status fine and does not need the record — fold in only if a concrete need appears.)
- [ ] Multi-machine resume: regenerate this state from a remote branch so a task can be picked up on another machine (issue #22). Needs a sync mechanism (git notes/refs) — explicitly out of scope for the local cache above.
- [x] Multi-machine resume — worktree + branch (issue #22): `cdd-worktree-resume [<branch>]` recreates a worktree on an **existing remote branch** on a second machine (no handoff required) and launches Claude Code for a resume-side command (`/cdd-process-pr`, `/cdd-merge-base`, `/cdd-pre-pr`). Discovery mode lists remote feature branches not already checked out. — `tools/cdd-worktree.sh` (new command + header), process doc §2.8/§2.13, `scripts/worktree-resume-assert.sh` (new, local-bare-repo-as-`origin` test) + CI step, README, both `CLAUDE.md` workflow sections.
- [ ] Multi-machine resume — handoff + state sync (issue #22): the resume above deliberately does **not** transfer the handoff (`<branch>.md`) or state record (`<branch>.state.json`); they stay local per machine. To carry them across, the chosen direction is a per-task ref namespace `refs/cdd/<branch>` holding the handoff + state JSON as a blob/tree, pushed by the helper/commands at lifecycle transitions and fetched + materialized by `cdd-worktree-resume` before launch, degrading gracefully when no ref exists. git notes (`refs/notes/cdd`) is the standard alternative but anchors metadata to a commit, so it must chase the moving branch tip — the branch-keyed ref avoids that. Committing these files into the work tree is rejected: they are deliberately out-of-tree, per-user artifacts.

**Milestone:** a task's lifecycle stage and its working sessions are recorded as data and surfaced by CDD tooling, not reconstructed by inference.
Loading
Loading