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
2 changes: 1 addition & 1 deletion .claude/commands/cdd-pre-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ If the project has no single runner yet, run each gate command in sequence inste
```
<!-- cdd-only-begin -->

**In this repo the runner is `./scripts/ci.sh`** — 26 gates, listed with what each checks in the registry at the top of the script; `./scripts/ci.sh list` names them and `./scripts/ci.sh <gate>` reruns one. CI runs it on Ubuntu and macOS, so a gate leaning on one host's sed/grep/awk can pass here and fail there. Not fail-fast. Two of its gates need interpretation rather than a rerun; see the sections below.
**In this repo the runner is `./scripts/ci.sh`** — 27 gates, listed with what each checks in the registry at the top of the script; `./scripts/ci.sh list` names them and `./scripts/ci.sh <gate>` reruns one. CI runs it on Ubuntu and macOS, so a gate leaning on one host's sed/grep/awk can pass here and fail there. Not fail-fast. Two of its gates need interpretation rather than a rerun; see the sections below.
<!-- cdd-only-end -->

## 3. Code review
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ This repo is documentation and shell scripts; there is no build step. Every chec
./scripts/ci.sh <gate> [<gate>] # rerun one gate while iterating on a failure
```

`scripts/ci.sh` is the **single source of the gate sequence** (process doc §2.14): CI (Ubuntu and macOS) and `/cdd-pre-pr` both call it, and neither holds a gate list. The 26 gates are the registry at the top of the script, one line each saying what it checks; each gate's script under `scripts/` also runs standalone. Needs bash >= 4, `shellcheck`, `jq` and `tar` — **a missing tool fails its gate, never skips it**. The run is not fail-fast. Mechanics: `doc/architecture/overview.md`.
`scripts/ci.sh` is the **single source of the gate sequence** (process doc §2.14): CI (Ubuntu and macOS) and `/cdd-pre-pr` both call it, and neither holds a gate list. The 27 gates are the registry at the top of the script, one line each saying what it checks; each gate's script under `scripts/` also runs standalone. Needs bash >= 4, `shellcheck`, `jq` and `tar` — **a missing tool fails its gate, never skips it**. The run is not fail-fast. Mechanics: `doc/architecture/overview.md`.

When `/cdd-pre-pr` runs in this repo, the "build / format / lint / test" gates collapse into `./scripts/ci.sh` plus a doc reconciliation pass.

Expand All @@ -69,11 +69,11 @@ When `/cdd-pre-pr` runs in this repo, the "build / format / lint / test" gates c
| `demo/` | Demo / dogfooding subsystem (third artifact) |
| `demo/seed/` | Filled-in "Markdown Renderer" project content (not template) |
| `demo/{setup,teardown}.sh` | Create/teardown demo & dogfood instances; `lib.sh` shared |
| `scripts/` | `ci.sh` (the check runner: the gate registry) + the gate scripts it calls — smoke assertions, install smoke, host-tool portability check, command-set drift check, prompt-seam check, roadmap-length check (with whitelists), adapter-conformance check, code-host ladder assertion, adapter-bindings assertion |
| `scripts/` | `ci.sh` (the check runner: the gate registry) + the gate scripts it calls — smoke assertions, install smoke, toolchain-sync assertion, host-tool portability check, command-set drift check, prompt-seam check, roadmap-length check (with whitelists), adapter-conformance check, code-host ladder assertion, adapter-bindings assertion |
| `.github/workflows/` | CI: `template-smoke.yml` delegates to `scripts/ci.sh` |
| `.claude/commands/` | This repo's own slash commands |
| `.cdd/` | This repo's own capability-adapter bindings: `code-host` and `tracker`, relative symlinks to the GitHub adapters under `tools/adapters/` |
| `tools/` | Bootstrap script + the canonical shared helpers (`cdd-worktree.sh`, `cdd-state.sh`, both self-installing) + the capability adapters under `adapters/<capability>/<backend>.sh` (mirroring `~/.cdd/adapters/<capability>`; none installs as a rung — `cdd-worktree.sh install` copies them to the `~/.cdd/tools/adapters/` library that downstream `.cdd/` shims exec) |
| `tools/` | Bootstrap script + the canonical shared helpers (`cdd-worktree.sh`, `cdd-state.sh`, both self-installing and self-updating) + the capability adapters under `adapters/<capability>/<backend>.sh` (mirroring `~/.cdd/adapters/<capability>`; none installs as a rung — `cdd-worktree.sh install` copies them to the `~/.cdd/tools/adapters/` library that downstream `.cdd/` shims exec) |

## Architecture

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ curl -fsSL https://raw.githubusercontent.com/drabaioli/cdd/main/tools/cdd-worktr

CDD's scripts need bash >= 4; macOS ships 3.2, so install Homebrew bash first (`brew install bash`). On an older bash the helper refuses to load, with a one-line message.

Either form wires `~/.bashrc` and `~/.zshrc` (idempotent); open a new shell afterwards. A project whose `.cdd/` binds a tracker or code host also needs the adapter library at `~/.cdd/tools/adapters/`: the checkout form installs it, while the curl form fetches only the helper and prints the one-line fetch for each adapter (see `template/BOOTSTRAP.md`). It spins up and tears down the per-task git worktree that a task's plan and implementation sessions run in, and `cdd-worktree-resume [<branch>]` recreates that worktree on a second machine — tracking the existing remote branch — so a task started elsewhere can be picked up to run `/cdd-implement`, `/cdd-process-pr`, `/cdd-merge-base`, or `/cdd-pre-pr`. The task's handoff, plan file and state record ride along too, synced through a per-task git ref (advisory — resume still works without them). `cdd-worktree-gc` periodically reaps the handoff, plan file, state record, and synced ref of tasks whose PR has merged (dry-run unless `--force`), so those artifacts don't accumulate across machines. Once a task's PR has merged, `cdd-worktree-done` (or, as backstop, `cdd-worktree-gc`) also closes the issues the task was sourced from, through the tracker, and links the merged PR on each it closes.
Either form wires `~/.bashrc` and `~/.zshrc` (idempotent); open a new shell afterwards. A project whose `.cdd/` binds a tracker or code host also needs the adapter library at `~/.cdd/tools/adapters/`: the checkout form installs it, while the curl form fetches only the helper and prints the one-line fetch for each adapter (see `template/BOOTSTRAP.md`). The checkout form keeps itself current: a pull of the checkout's default branch reinstalls the helpers when they changed, and open shells pick up the new code on their next command. Without a checkout, `bash ~/.cdd/tools/cdd-worktree.sh update` brings the helpers and the adapter library up to date with upstream `main`. It spins up and tears down the per-task git worktree that a task's plan and implementation sessions run in, and `cdd-worktree-resume [<branch>]` recreates that worktree on a second machine — tracking the existing remote branch — so a task started elsewhere can be picked up to run `/cdd-implement`, `/cdd-process-pr`, `/cdd-merge-base`, or `/cdd-pre-pr`. The task's handoff, plan file and state record ride along too, synced through a per-task git ref (advisory — resume still works without them). `cdd-worktree-gc` periodically reaps the handoff, plan file, state record, and synced ref of tasks whose PR has merged (dry-run unless `--force`), so those artifacts don't accumulate across machines. Once a task's PR has merged, `cdd-worktree-done` (or, as backstop, `cdd-worktree-gc`) also closes the issues the task was sourced from, through the tracker, and links the merged PR on each it closes.

## Questions?

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 0014: Keep the install current by syncing on pull

**Status:** Accepted

## Context

The shared helpers install as a *copy* under `~/.cdd/tools/` (process doc §2.8), so that the
commands keep working when no CDD checkout is around. The cost of a copy is that it goes stale.
Merging a helper fix updates nothing on a machine until someone re-runs `install`, and nothing
says so. Issue #124 was fixed by PR #125, and `cdd-worktree-done` then hit the fixed bug again
about 40 seconds after the merge: the install still held the old script. Three helper PRs in a
row (#122, #123, #125) had touched the helpers, so this is the normal case, not a rare one.

Two more gaps sit next to it:

- **Open shells.** A shell (or tmux pane) that sourced the helpers keeps the functions it
sourced, even after a reinstall.
- **Machines without a checkout.** A machine that installed with curl can only update by
re-running three long curl incantations, and has nothing to compare against.

The rule to respect is §2.8's: the helper "probes the ground truth rather than assuming or
versioning", and installs are "newest wins, always from latest `main`".

## Decision

Three small pieces, none of which runs in the background or reaches the network on its own:

1. **Sync on pull.** `install`, run from a CDD git checkout, writes a managed `post-merge` and
`post-rewrite` hook into that checkout (between them, every `git pull` variant). After a
pull that lands on the default branch, the hook runs the just-pulled checkout's own
`cdd-worktree.sh sync`, which reinstalls both helpers and the adapter library when any of
them differs from the install — by content (`cmp`), never a recorded version — and prints
one line. Pulls on other branches never install. The hook **reinstalls**, it does not just
warn: reinstalling is a mechanical step with nothing to decide, and §2.8 already says the
newest always wins. A hook the user wrote is never overwritten; under `core.hooksPath`
nothing is written. Both cases print a note. `cdd-worktree-done` already pulls the default
branch, so finishing a task updates the install.
2. **Self-reloading shells.** Each helper records its file's `cksum` when sourced. Each public
command re-sources the file when it has changed and re-dispatches to the new definition, in
the same shell, silently.
3. **One update command.** `bash ~/.cdd/tools/cdd-worktree.sh update` fetches upstream `main`'s
`tools/` (a shallow, blobless, sparse clone), compares it with the install, and installs it
when anything differs. It runs only when asked. `cdd-state`'s unknown-verb and invalid-stage
errors, the usual symptom of a project newer than its helper, name it.

Rejected:

- **A version stamp** in the install. It violates §2.8, and a content comparison costs nothing.
- **A symlink install** (`~/.cdd/tools/*` pointing into the checkout). It needs no code, but it
breaks "works without a live checkout", follows whatever branch the main worktree has checked
out, and dangles once `cdd-worktree-done` removes the feature worktree it came from.
- **Warning at every command** (the first plan): `install` records its source checkout, and
every `cdd-worktree*` command compares that checkout with the install. It also had a
throttled background fetch of upstream for curl installs, a scan of the project's command
prompts for `cdd-state` verbs the helper lacks, and hooks written into downstream clones. It
worked, but it was rejected at plan approval as too heavy and too invasive. The hook makes the
per-command comparison redundant, and the loud `cdd-state` error now explains itself.
- **A `post-checkout` hook.** No pull needs it, and it fires on every branch switch.
- **Re-running `done` after its own pull** to pick up a fix mid-run. `done` is not re-entrant
once it has moved to the main worktree.

## Consequences

- Merging a helper PR and pulling `main` is enough: the next command anywhere on the machine
runs the new code. Nothing prints while the install is current.
- The hook writes `~/.cdd/tools` during a `git pull`. That is its purpose, and it happens only
on the default branch of a checkout the user installed from.
- Known limits: the one command whose own pull brings a fix (`done`) still runs the old code to
the end. Shells opened before this change have no reload guard and need one restart. A
checkout first needs one manual `install` to get its hook. A curl install stays as old as its
last `update`.
- Downstream projects get nothing new: no hooks, no command changes. The template tells them
how the install stays current.
- The gates that run `install` now install from a copy of `tools/` outside any repo, so a check
run never hooks the developer's checkout. A new `toolchain-sync` gate covers the hook, the
reload and `update` (against a local stub upstream).
1 change: 1 addition & 0 deletions doc/architecture/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ How this repo is structured. This index is a pointer list — the content lives
- [`0011-bind-adapters-through-a-machine-global-library.md`](adr/0011-bind-adapters-through-a-machine-global-library.md) — how an installer binds a project to a shipped adapter: `cdd-worktree.sh install` copies the adapters to a machine-global library that is not a ladder rung, and the committed `.cdd/<capability>` shim execs it through `$HOME`; a missing library is a broken adapter; the installers write the project rung only, never a secret
- [`0012-drop-the-builtin-gh-rung.md`](adr/0012-drop-the-builtin-gh-rung.md) — dropping the built-in `gh` rung now that installers bind adapters: the ladder ends at "nothing", a missing adapter skips the feature with one `/cdd-retrofit` line (or stops where the feature is the invocation's point), per-call-site behaviour for the prompts and `done`/`gc`/`list`/`resume`, and the read-only adapter verbs that replace the `gh` settings allowlist
- [`0013-same-list-same-scripts-and-host-tool-portability.md`](adr/0013-same-list-same-scripts-and-host-tool-portability.md) — "same list, same scripts", not "same command"; a `portability` gate and Ubuntu + macOS CI; bash >= 4; a missing tool fails its gate
- [`0014-keep-the-install-current-by-syncing-on-pull.md`](adr/0014-keep-the-install-current-by-syncing-on-pull.md) — keeping the installed helpers current: a sync hook in the CDD checkout reinstalls on a default-branch pull (content compare, never a version stamp), shells self-reload, `cdd-worktree.sh update` for checkout-less machines; why not a symlink install or per-command warnings
Loading
Loading