Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Integrate the current state of `main` into the feature branch. Two phases: a **dry-run conflict assessment** first, then on user approval the actual merge with conflict resolution.
Integrate the current state of the base branch into the feature branch. Two phases: a **dry-run conflict assessment** first, then on user approval the actual merge with conflict resolution.

Run this command on the feature branch (not on main). Use it when:
Run this command on the feature branch (not on the base branch). Use it when:

- `main` has advanced under your feature branch and you want to integrate before opening or merging the PR.
- Something useful has landed on main (a new utility, a refactor, an updated convention) that this branch should pick up without a separate roadmap task.
- The base branch has advanced under your feature branch and you want to integrate before opening or merging the PR.
- Something useful has landed on the base branch (a new utility, a refactor, an updated convention) that this branch should pick up without a separate roadmap task.

## 0. Resolve the default branch

```bash
DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || echo main)
```

Use `$DEFAULT_BRANCH` everywhere `main`/`origin/main` appeared in earlier versions of this command. All git commands below use this variable.

> **Scope note:** this detects the hosting platform's default branch. If the project uses a gitflow model where the platform default is a release branch and daily work targets a different integration branch (e.g. platform default is `main` but features branch off `devel`), this command targets the wrong branch. That case requires an explicit `BASE_BRANCH` config and is tracked as a separate roadmap item.

## 1. Sanity check

Confirm the current branch is not `main`:
Confirm the current branch is not the base branch:

```bash
git rev-parse --abbrev-ref HEAD
Expand All @@ -21,32 +31,32 @@ git status --porcelain

If there are uncommitted changes, stop and ask the user whether to stash or commit before continuing. Do not merge over uncommitted work.

## 2. Update local main reference
## 2. Update local base branch reference

```bash
git fetch origin main
git fetch origin "$DEFAULT_BRANCH"
```

Determine how far the branch has diverged from `origin/main`:
Determine how far the branch has diverged from `origin/$DEFAULT_BRANCH`:

```bash
git log --oneline HEAD..origin/main | head -50
git log --oneline origin/main..HEAD | head -50
git log --oneline "HEAD..origin/$DEFAULT_BRANCH" | head -50
git log --oneline "origin/$DEFAULT_BRANCH..HEAD" | head -50
```

Report:

- Number of commits on `origin/main` not in this branch.
- Number of commits on this branch not in `origin/main`.
- Number of commits on `origin/$DEFAULT_BRANCH` not in this branch.
- Number of commits on this branch not in `origin/$DEFAULT_BRANCH`.

If there is nothing on `origin/main` not in this branch, there is nothing to merge. Stop and report.
If there is nothing on `origin/$DEFAULT_BRANCH` not in this branch, there is nothing to merge. Stop and report.

## 3. Dry-run conflict assessment

Perform a non-committing test merge to surface conflicts without mutating the working tree:

```bash
git merge-tree --write-tree --name-only origin/main HEAD
git merge-tree --write-tree --name-only "origin/$DEFAULT_BRANCH" HEAD
```

Capture the list of conflicting files.
Expand All @@ -60,18 +70,18 @@ If there are conflicts, for each conflicting file:

- Read both versions and the merge base. Use:
```bash
git show origin/main:<file>
git show "origin/$DEFAULT_BRANCH:<file>"
git show HEAD:<file>
git show $(git merge-base origin/main HEAD):<file>
git show "$(git merge-base "origin/$DEFAULT_BRANCH" HEAD):<file>"
```
- Classify the conflict:
- **Mechanical**: textual collision in a region where the intent is obvious (e.g. both sides added an import, both sides added an entry to the same list, formatting drift).
- **Logical**: the two sides changed the same logical concern in incompatible ways (e.g. one renamed a function the other modified the body of).
- **Structural**: file moved/renamed/deleted on one side and modified on the other.

Also scan the non-conflicting changes on `origin/main` for items relevant to this branch:
Also scan the non-conflicting changes on `origin/$DEFAULT_BRANCH` for items relevant to this branch:

- New conventions established on main that this branch's code should adopt.
- New conventions established on the base branch that this branch's code should adopt.
- New utilities or helpers that obviate code on this branch.
- Refactored interfaces that this branch consumes.

Expand All @@ -80,10 +90,10 @@ Also scan the non-conflicting changes on `origin/main` for items relevant to thi
Present to the user:

```
## Merge-main assessment
## Merge-base assessment

Commits to integrate from origin/main: <N>
Commits unique to this branch: <M>
Commits to integrate from origin/<DEFAULT_BRANCH>: <N>
Commits unique to this branch: <M>

### Conflicts
- <file>: <mechanical | logical | structural>, <one-line description>
Expand All @@ -107,7 +117,7 @@ If conflicts are non-trivial, **stop and wait for explicit user approval**. Do n
On user approval:

```bash
git merge origin/main
git merge "origin/$DEFAULT_BRANCH"
```

Resolve conflicts file by file:
Expand Down Expand Up @@ -135,7 +145,7 @@ For each non-conflict item flagged in step 3 (new conventions, helpers, refactor
If applied, commit separately from the merge commit with a message like:

```
adopt: <one-line description of what was adopted from main>
adopt: <one-line description of what was adopted from the base branch>
```

## 7. Verify
Expand All @@ -149,7 +159,7 @@ If anything fails, report the failure and stop. Do not push.
Present a final summary:

```
## Merge-main summary
## Merge-base summary
- [ ] Merge completed
- [ ] Conflicts resolved: <count> (mechanical: M, logical: L, structural: S)
- [ ] Improvements adopted: <count or "none">
Expand All @@ -159,4 +169,4 @@ Present a final summary:
Next: re-run /cdd-pre-pr before opening or updating the PR.
```

The user should re-run `/cdd-pre-pr` in a fresh session after `/cdd-merge-main` to ensure the merged state passes all gates.
The user should re-run `/cdd-pre-pr` in a fresh session after `/cdd-merge-base` to ensure the merged state passes all gates.
10 changes: 8 additions & 2 deletions .claude/commands/cdd-next-step.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,20 @@ mkdir -p ~/.claude-handoffs/cdd

## 8. Print the next command

After writing, print exactly:
After writing, resolve the repo root:

```bash
REPO_ROOT=$(git rev-parse --show-toplevel)
```

Then print exactly (substituting the actual `$REPO_ROOT` path, not a placeholder):

```
Handoff written: ~/.claude-handoffs/cdd/<branch>.md
Next: cdd-worktree <branch>

If `cdd-worktree` reports "command not found", the worktree helper isn't sourced. Add this to ~/.bashrc (or ~/.zshrc) and open a new shell:
[[ -f "$HOME/Code/cdd/tools/cdd-worktree.sh" ]] && source "$HOME/Code/cdd/tools/cdd-worktree.sh"
[[ -f "<REPO_ROOT>/tools/cdd-worktree.sh" ]] && source "<REPO_ROOT>/tools/cdd-worktree.sh"
```

The user will close this session, run `cdd-worktree <branch>` from the main worktree, and a fresh Claude session will open in the new worktree with the first prompt already submitted.
22 changes: 15 additions & 7 deletions .claude/commands/cdd-pre-pr.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
Run a pre-PR checklist for the current branch. Compare against `main` to identify all changes. This is a verification session: it runs CI gates, code-reviews the diff, and reconciles documentation against the changes.
Run a pre-PR checklist for the current branch. Compare against the base branch to identify all changes. This is a verification session: it runs CI gates, code-reviews the diff, and reconciles documentation against the changes.

This session is **fresh and separate** from the implementation session by design, so that the verification work is not biased by the context that produced the change. Any "propose to the user" step in this command is a proposal to the user running this session.

## 0. Resolve the default branch

```bash
DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || echo main)
```

Use `$DEFAULT_BRANCH` wherever `main`/`origin/main` appears in git commands below.

## 1. Identify changes

```bash
git diff main...HEAD --name-only
git diff "$DEFAULT_BRANCH"...HEAD --name-only
git status --porcelain
```

Expand Down Expand Up @@ -77,11 +85,11 @@ Do **not** propose generic CI improvements every run. The default is silence. If
## 7. Upstream drift check

```bash
git fetch origin main
git log --oneline HEAD..origin/main
git fetch origin "$DEFAULT_BRANCH"
git log --oneline "HEAD..origin/$DEFAULT_BRANCH"
```

If `origin/main` has advanced beyond the branch point, mention it and recommend running `/cdd-merge-main` before opening the PR. Do not merge from this session.
If `origin/$DEFAULT_BRANCH` has advanced beyond the branch point, mention it and recommend running `/cdd-merge-base` before opening the PR. Do not merge from this session.

<!-- cdd-only-begin -->
## Command-set drift (CDD repo only)
Expand Down Expand Up @@ -117,7 +125,7 @@ Present a checklist summary:
- [ ] Roadmap up to date
- [ ] New behaviour tested (or untested-with-reason recorded)
- [ ] CI gaps surfaced: none / proposed (list them)
- [ ] No upstream drift (or: /cdd-merge-main recommended)
- [ ] No upstream drift (or: /cdd-merge-base recommended)
- [ ] Reconciliation edits committed
```

Expand Down Expand Up @@ -151,7 +159,7 @@ gh auth status && git remote get-url origin # origin should be a github.com UR

If either is missing, say so in one line and skip this step (the checklist above still stands).

If §7 found upstream drift, restate the recommendation to run `/cdd-merge-main` before opening the PR, and let the user decide whether to proceed anyway.
If §7 found upstream drift, restate the recommendation to run `/cdd-merge-base` before opening the PR, and let the user decide whether to proceed anyway.

Ask: **"Open a PR now?"** Do not pre-show a title or body, and do not print manual `gh` instructions — just ask whether to proceed.

Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/cdd-process-pr.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Address the open PR's review feedback: read the review comments for the current branch, triage them, implement the change-requests (pushing back where warranted), then auto-post in-thread replies and auto-commit + push the result.

Run this command on the feature branch (not on main), after a PR has been opened and someone has reviewed it. It is a post-review side-loop, analogous in position to `/cdd-merge-main`.
Run this command on the feature branch (not on main), after a PR has been opened and someone has reviewed it. It is a post-review side-loop, analogous in position to `/cdd-merge-base`.

**Note on automation:** this command has a single checkpoint, placed up front: the triage plan in step 4. Once the user approves that plan, the rest of the run — edits, in-thread replies, commit, push — executes without further confirmation gates (see the process doc, "The `/cdd-process-pr` exception"). Do not add per-action gates after the plan is approved. Review threads are never resolved by this command; the user resolves them.

Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/cdd-retrofit.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Walk every file in `$STAGE/render`. All writes go into `$WT` (the isolated workt

- **Absent in `$WT`** → copy it directly (create parent dirs as needed). This covers the slash commands, doc skeletons, the worktree helper, `.claude/settings.json`, and the marker in the common case.
- **Present in `$WT`** (collision — typically `CLAUDE.md`, sometimes `doc/` files or `.claude/settings.json`) → propose a merge interactively, one file at a time:
- `CLAUDE.md`: keep the project's existing content; propose adding the CDD pieces it lacks (the Key references table rows for `doc/`, and the Workflow section referencing `/cdd-next-step`, `/cdd-pre-pr`, `/cdd-merge-main`). Show the proposed result; apply only on approval.
- `CLAUDE.md`: keep the project's existing content; propose adding the CDD pieces it lacks (the Key references table rows for `doc/`, and the Workflow section referencing `/cdd-next-step`, `/cdd-pre-pr`, `/cdd-merge-base`). Show the proposed result; apply only on approval.
- `.claude/settings.json`: merge the `permissions.allow` arrays (union); show the result before writing.
- Anything else: show both versions and propose the merge; the user decides per file.
- Never delete or move existing files.
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/template-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ jobs:
- name: Show the scaffold commit
run: git -C /tmp/smoke/demo-project log --oneline

- name: Bootstrap a CamelCase-dir project (Fix 2 coverage)
run: |
./tools/bootstrap-cdd-project.sh \
--name "My CamelCase Project" \
--slug myproj \
--path /tmp/smoke/MyProject
./scripts/template-smoke-assert.sh /tmp/smoke/MyProject

- name: Stage a render-only tree and assert it is clean
run: |
./tools/bootstrap-cdd-project.sh --stage \
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,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>`.
- **When main has advanced under a feature branch** (merge session): run `/cdd-merge-main` in a fresh context on the feature branch.
- **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.
- Keep the process doc, template, and roadmap consistent as part of every change. Process-first, then template.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A task flows through up to five sessions, each driven by one slash command:
flowchart TD
NS("Handoff session<br>/cdd-next-step, on the main worktree"):::agent
IMPL("Implementation session<br>opens in plan mode — ③ plan approved —<br>implement, update docs + roadmap, commit"):::agent
MM("Merge session<br>/cdd-merge-main: integrate main, dry-run first"):::opt
MM("Merge session<br>/cdd-merge-base: integrate main, dry-run first"):::opt
PP("Pre-PR session<br>/cdd-pre-pr: CI gates, code review, doc reconciliation"):::agent
REV("Human reviews the PR on GitHub"):::human
PPR("PR-review session<br>/cdd-process-pr: triage + address review comments"):::opt
Expand Down
4 changes: 2 additions & 2 deletions demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This directory is a **third artifact** of the CDD repo, alongside `template/` an

The `demo/` subsystem serves two purposes from one shared seed:

- **Demo** — a reproducible, visual walkthrough of CDD's task cycle: one reviewable PR, two parallel branches that conflict, and a `/cdd-merge-main` that resolves the conflict *and* delivers a dependency.
- **Demo** — a reproducible, visual walkthrough of CDD's task cycle: one reviewable PR, two parallel branches that conflict, and a `/cdd-merge-base` that resolves the conflict *and* delivers a dependency.
- **Dogfooding** — a real greenfield project (CDD roadmap Phase 2) so working past the demo is genuine work.

The seed project is **Markdown Renderer**: a small local Flask app where you paste Markdown, see it rendered live, and copy the result as **rich text** so it pastes formatted into Gmail, Google Docs, or Word — not as raw `# Heading` source. The key technical spine: pasting into email clients needs `text/html` on the clipboard, and email strips `<style>` blocks, so formatting only survives if CSS is **inlined** (`<p style="...">`). That inline-styling step is the heart of Phases 2–3.
Expand Down Expand Up @@ -89,7 +89,7 @@ The roadmap (`demo/seed/doc/knowledge_base/roadmap.md`) is designed so a fresh i
- **Branch A (Phase 2):** add `inline_styles(html)` and a `CopyEmailSafeAction`, registered in the `ACTIONS` list and a button in the toolbar.
- **Branch B (Phase 3):** add `ExportStandaloneAction` / `ExportEmailAction`, registered in the **same** `ACTIONS` region and the **same** toolbar region — a guaranteed merge conflict. Branch B's email export depends on Phase 2's `inline_styles()`, which doesn't exist on the branch yet, so it ships the standalone export and leaves the email export blocked.

3. **Merge — `/cdd-merge-main` does two jobs.** Land PR2 (Phase 2) first; `main` advances. On branch B, run `/cdd-merge-main`: it (a) resolves the `ACTIONS`/toolbar conflict with Phase 2 **and** (b) brings `inline_styles()` onto the branch, unblocking the email export. This shows `/cdd-merge-main` resolving a conflict *and* delivering a dependency — not a trivial fast-forward.
3. **Merge — `/cdd-merge-base` does two jobs.** Land PR2 (Phase 2) first; `main` advances. On branch B, run `/cdd-merge-base`: it (a) resolves the `ACTIONS`/toolbar conflict with Phase 2 **and** (b) brings `inline_styles()` onto the branch, unblocking the email export. This shows `/cdd-merge-base` resolving a conflict *and* delivering a dependency — not a trivial fast-forward.

## Verifying the subsystem

Expand Down
2 changes: 1 addition & 1 deletion demo/seed/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ This project uses the Claude-Driven Development workflow.

- **Before opening a PR**: run `/cdd-pre-pr` to verify CI gates pass and that architecture/feature docs and the roadmap reflect the change.
- **To start a new task**: run `/cdd-next-step` from the main worktree to produce a handoff, then run `<PROJECT_SLUG>-worktree <branch>` to spin up the implementation worktree.
- **When main has advanced under a feature branch**: run `/cdd-merge-main` from the feature branch.
- **When main has advanced under a feature branch**: run `/cdd-merge-base` from the feature branch.
- Keep `doc/architecture/`, `doc/features/`, and this file current as part of every change.
Loading
Loading