Skip to content

feat(workspace): bootstrap worktrees by default (closes #50)#51

Merged
chubes4 merged 1 commit into
mainfrom
feat-worktree-bootstrap
Apr 24, 2026
Merged

feat(workspace): bootstrap worktrees by default (closes #50)#51
chubes4 merged 1 commit into
mainfrom
feat-worktree-bootstrap

Conversation

@chubes4
Copy link
Copy Markdown
Member

@chubes4 chubes4 commented Apr 24, 2026

Summary

worktree add now runs detected setup steps by default so the new checkout is immediately test/build-ready. Pass --skip-bootstrap to opt out for pure-read use. Closes #50.

# Default: bootstrapped, ready to test/build
wp datamachine-code workspace worktree add <repo> <branch>

# Opt out for a bare checkout
wp datamachine-code workspace worktree add <repo> <branch> --skip-bootstrap

Why on by default

The earlier draft of this PR shipped --bootstrap as opt-in (matching the wording in the issue). Reconsidered after discussion: worktrees are overwhelmingly created to do branch work (run tests, build, commit, push), not to read code. Read-only consumers can use the primary checkout, which is already read-only by default. Making bootstrap opt-in forces every new agent to know about the flag or they silently hit the exact trap the issue describes (vitest can't load spec, sh: nx: command not found).

Making it on-by-default with a --skip-bootstrap escape hatch mirrors the existing inject_context=true + --skip-context-injection convention for agent-memory injection from #45. Consistent mental model across worktree setup surfaces.

What bootstrap does

Three independent steps, in order, each skipped gracefully when trigger file or tool is missing:

  1. git submodule update --init --recursive — if .gitmodules exists
  2. Package-manager install — based on lockfile priority (pnpm > bun > yarn > npm):
    • pnpm-lock.yamlpnpm install --frozen-lockfile
    • bun.lockb / bun.lockbun install --frozen-lockfile
    • yarn.lockyarn install --immutable
    • package-lock.jsonnpm ci
  3. composer install --no-interaction --prefer-dist — if composer.lock exists

A repo with package.json but no lockfile is treated as "no package-manager step" — we never guess which manager to use.

Safety model

  • Graceful degradation. Missing tool on $PATH reports skipped with a reason, not an error. Missing trigger file = skipped.
  • No rollback on failure. A failed step is surfaced as FAILED in the result and shown as a CLI warning, but the worktree itself stays created so the user can retry manually. Matches the existing context_injected=false + context_skip_reason=… precedent from Inject site-agent context into worktrees on creation #45.
  • No WordPress coupling. WorktreeBootstrapper is a pure class so tests/smoke-worktree-bootstrap.php exercises it without a WP bootstrap.

Output

Success: Worktree "data-machine@feat-foo" added at … (branch feat-foo).
Handle: data-machine@feat-foo
Path:   /Users/…/data-machine@feat-foo
Branch: feat-foo (created)
Context: injected (2 files)
  - …/.claude/CLAUDE.local.md
  - …/.opencode/AGENTS.local.md
Bootstrap: ok
  - submodules skipped (no .gitmodules)
  ✓ packages   ran: npm ci
  ✓ composer   ran: composer install --no-interaction --prefer-dist

Files

  • New: inc/Workspace/WorktreeBootstrapper.php — detect + bootstrap + format helpers
  • New: tests/smoke-worktree-bootstrap.php — 30 assertions covering detection priority, skip behavior, real-repo submodule run, failure formatting
  • Modified: inc/Workspace/Workspace.php — new $bootstrap param on worktree_add() (default true)
  • Modified: inc/Abilities/WorkspaceAbilities.phpbootstrap input on datamachine/workspace-worktree-add (default true) + output schema
  • Modified: inc/Cli/Commands/WorkspaceCommand.php--skip-bootstrap flag, help block, result rendering
  • Modified: tests/TESTING.md — new section 3a for the CLI smoke

Testing

Pure smokes (62/62 passing):

$ php tests/smoke-worktree-bootstrap.php
30/30 passed

$ php tests/smoke-worktree-handles.php
32/32 passed

Covers: detect() priority (pnpm > bun > yarn > npm), skip on empty dir, real-repo submodule run, format() output shape (ran + failed + skipped markers, failure output tail).

Live CLI tests on intelligence-chubes4 Studio site:

Scenario Repo Result
Default (no flag) on Node+PHP repo data-machine ✓ submodules skipped, npm ci ran, composer install ran, node_modules/ + vendor/ populated
--skip-bootstrap data-machine-code ✓ no bootstrap block shown, no vendor/ in worktree
Default on PHP-only repo data-machine-code ✓ submodules skipped, packages skipped (no lockfile), composer ran
--skip-context-injection default-bootstrap data-machine-code ✓ context skipped, bootstrap ran

Out of scope (follow-ups)

AI assistance

  • AI assistance: Yes
  • Tool(s): Claude Code (Opus 4.7)
  • Used for: Drafted the full implementation (detector + runner + CLI wiring + smoke test + TESTING.md update). Chris challenged the initial opt-in default, which was flipped to on-by-default with a --skip-bootstrap escape hatch. Chris ran live worktree-add scenarios on data-machine and data-machine-code from the intelligence-chubes4 Studio site to confirm end-to-end behavior for both defaults and the escape hatch.

@chubes4 chubes4 force-pushed the feat-worktree-bootstrap branch from c69c9ce to 0bb064f Compare April 24, 2026 13:26
`worktree add` now runs detected setup after creating the checkout so it
is immediately test/build-ready. Each step is skipped gracefully when
its trigger file or tool is missing:

  1. `git submodule update --init --recursive` when `.gitmodules` exists
  2. Package-manager install based on lockfile priority (pnpm > bun > yarn > npm):
       pnpm-lock.yaml    → pnpm install --frozen-lockfile
       bun.lockb/.lock   → bun install --frozen-lockfile
       yarn.lock         → yarn install --immutable
       package-lock.json → npm ci
  3. `composer install --no-interaction --prefer-dist` when `composer.lock` exists

Defaults to on because worktrees are overwhelmingly created to do branch
work — run tests, build, commit, push — not to read code. Read-only
consumers should use the primary checkout (which is already read-only by
default). Pass `--skip-bootstrap` to create a bare worktree when you
really only need to read.

A failed bootstrap step is surfaced in the result with exit code and
output tail, but does NOT roll back the worktree. The checkout stays
created so the user can retry manually.

Changes:
- New `DataMachineCode\\Workspace\\WorktreeBootstrapper` class (pure PHP,
  no WP dependency) with detect / bootstrap / format helpers.
- `Workspace::worktree_add()` gains a `$bootstrap` param (default true).
- Ability `datamachine/workspace-worktree-add` gains a `bootstrap` input
  (default true) and surfaces a `bootstrap` object in the output schema.
- CLI `worktree add` gains `--skip-bootstrap` (matches the existing
  `--skip-context-injection` convention).
- Success output includes a `Bootstrap: ok` block listing each step.

Tests:
- `tests/smoke-worktree-bootstrap.php` — 30 pure-PHP assertions covering
  detection priority, skip behavior, a real-repo submodule run, and
  failure formatting.
- `tests/TESTING.md` — new section 3a with CLI smoke steps for both
  default-on and `--skip-bootstrap` paths.

Follow-ups (out of scope per issue #50):
- `.datamachine/worktree.yml` manifest for repo-declared custom chains
- Teardown on `worktree remove`
- pnpm-style content-addressed `node_modules` sharing
@chubes4 chubes4 force-pushed the feat-worktree-bootstrap branch from 0bb064f to 73527eb Compare April 24, 2026 13:27
@chubes4 chubes4 changed the title feat(workspace): opt-in --bootstrap for worktree add (closes #50) feat(workspace): bootstrap worktrees by default (closes #50) Apr 24, 2026
@chubes4 chubes4 merged commit 844902e into main Apr 24, 2026
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.

Worktree add doesn't bootstrap dependencies (node_modules, submodules)

1 participant