Skip to content

fix(worktree): npm install only where npm is the package manager (VST-340) - #1451

Merged
Brad (bmethod) merged 13 commits into
mainfrom
vst-340
Aug 17, 2026
Merged

fix(worktree): npm install only where npm is the package manager (VST-340)#1451
Brad (bmethod) merged 13 commits into
mainfrom
vst-340

Conversation

@bmethod

Copy link
Copy Markdown
Collaborator

Fixes VST-340 (issue #1417): create ran npm install unconditionally in any worktree with a package.json, so a pnpm workspace (drovr, pnpm 10.33.2) got a wrong-tree install AND a stray package-lock.json — every fresh worktree started dirty, tripping git status --porcelain gates, and git add -A would have committed an npm lockfile into a pnpm monorepo.

install_worktree_dependencies now detects the manager first: a pnpm-lock.yaml/yarn.lock/bun.lockb/bun.lock beside package.json, or a packageManager pin naming pnpm/yarn/bun, skips the step (those repos provision their own dependencies). The historical npm path survives only where npm is genuinely the manager, and a failed install now names its log file instead of vanishing into a silenced background job.

New worktree_js_dependency_install.sh suite (4 pins, red-first: both skip pins fail against the old script; the plain-npm pin is the must-still-install control). Neighbor worktree suites pass.

Closes VST-340.
Closes #1417.

https://claude.ai/code/session_012epxJEzGqT7q3qcFhdZUt5

Copilot AI balanced review requested due to automatic review settings August 17, 2026 21:02
@linear-code

linear-code Bot commented Aug 17, 2026

Copy link
Copy Markdown

VST-340

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 17, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 17, 2026

Copy link
Copy Markdown

Approvability

Verdict: Approved 66c99d4

Bug fix with clear scope: prevents npm install from running in non-npm workspaces (pnpm/yarn/bun). Changes are well-tested with 11 test scenarios covering edge cases. The review comment about unreadable-pin handling was addressed in this commit.

You can customize Macroscope's approvability policy. Learn more.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates worktree create JS dependency installation to avoid running npm install in non-npm workspaces (pnpm/yarn/bun), and adds coverage to prevent regressions.

Changes:

  • Skip background npm install when a pnpm/yarn/bun lockfile exists or packageManager pins pnpm/yarn/bun
  • Improve failure visibility for npm install by writing output to a temp log and printing its path on failure
  • Add a bash test to verify npm install is skipped/ran in the expected repository setups

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tools/size-ratchet-baseline.tsv Updates the ratchet baseline for the worktree script size change.
skills/worktree/tests/worktree_js_dependency_install.sh Adds test coverage for conditional npm installation behavior in worktree create.
skills/worktree/scripts/worktree Implements conditional npm install logic and failure logging for JS worktrees.
CHANGELOG.md Documents the updated worktree dependency-install behavior and failure visibility.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread skills/worktree/tests/worktree_js_dependency_install.sh
Comment thread skills/worktree/tests/worktree_js_dependency_install.sh Outdated
Comment thread skills/worktree/scripts/worktree Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b176684310

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread skills/worktree/scripts/worktree Outdated
Copilot AI review requested due to automatic review settings August 17, 2026 21:07
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 17, 2026 21:07

Dismissing prior approval to re-evaluate c025a4a

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 17, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 17, 2026 21:08

Dismissing prior approval to re-evaluate b041561

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

skills/worktree/scripts/worktree:1745

  • The npm packageManager detection only matches "npm@", so "packageManager": "npm" (no version) won’t be treated as explicit npm evidence and can incorrectly skip npm install when a foreign lockfile exists. Consider matching "npm" with an optional @... (and similarly anchoring the regex to the key position, e.g. ^[[:space:]]*"packageManager"), so JSON strings elsewhere don’t accidentally satisfy the pattern.
  if [[ ! -f "$wt/package-lock.json" ]] &&
    ! grep -Eq '"packageManager"[[:space:]]*:[[:space:]]*"npm@' "$wt/package.json" 2>/dev/null; then
    if [[ -f "$wt/pnpm-lock.yaml" || -f "$wt/yarn.lock" || -f "$wt/bun.lockb" || -f "$wt/bun.lock" ]]; then
      return 0
    fi
    if grep -Eq '"packageManager"[[:space:]]*:[[:space:]]*"(pnpm|yarn|bun)' "$wt/package.json" 2>/dev/null; then
      return 0

skills/worktree/tests/worktree_js_dependency_install.sh:61

  • wait_for_installs always returns success even on timeout, which makes failures harder to diagnose and forces callers to add extra sleep calls plus ad-hoc greps later. Consider returning non-zero on timeout (and having callers fail with a clear message), or renaming the helper to reflect that it is a best-effort delay rather than a wait with a contract.
wait_for_installs() {
  # The npm path is backgrounded; give it a moment to land.
  local deadline=$((SECONDS + 5))
  while [ "$SECONDS" -lt "$deadline" ]; do
    [ -s "$NPM_CALL_LOG" ] && return 0
    sleep 0.2
  done
  return 0
}

skills/worktree/tests/worktree_js_dependency_install.sh:6

  • The opening comment is a bit ambiguous (“create runs …”) without naming the command/script under test. Suggest rephrasing to explicitly reference the worktree create command (and optionally the install_worktree_dependencies behavior) so future readers immediately understand what “create” refers to.
# create runs npm install only where npm is genuinely the package manager.
# In a pnpm/yarn/bun workspace the historical unconditional `npm install`
# resolved the wrong tree and wrote a stray package-lock.json into the fresh
# checkout — dirty from birth, and `git add -A` would commit an npm lockfile
# into a pnpm monorepo.

Copilot AI review requested due to automatic review settings August 17, 2026 21:09
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (3)

skills/worktree/scripts/worktree:1750

  • yarn.lock is treated as definitive evidence to skip npm install unless there is an npm lockfile or an explicit npm packageManager pin. This will incorrectly skip installs for npm-based repos that don’t commit package-lock.json but happen to include a yarn.lock (e.g., leftover or tooling-generated). Consider making the skip decision require positive evidence of yarn usage (e.g., packageManager: "yarn@...", .yarnrc.yml, .yarn/, or similar) rather than yarn.lock alone, or treat yarn.lock as “weak evidence” that doesn’t block npm unless other yarn indicators are present.
  if [[ ! -f "$wt/package-lock.json" ]] &&
    ! grep -Eq '"packageManager"[[:space:]]*:[[:space:]]*"npm@' <<<"$pkg_flat"; then
    if [[ -f "$wt/pnpm-lock.yaml" || -f "$wt/yarn.lock" || -f "$wt/bun.lockb" || -f "$wt/bun.lock" ]]; then
      return 0
    fi

skills/worktree/tests/worktree_js_dependency_install.sh:74

  • The production worktree script backgrounds the npm install, so limiting the “no npm invocation” assertion window to 2 seconds can produce false positives if the backgrounded call happens after the 2-second window. To make this test reliably detect unwanted npm usage, increase the observation window (e.g., 5–10 seconds) and/or add a second check after a short delay to catch late invocations.
assert_log_stays_empty() { # NAME — watch 2s and fail the moment npm logs
  local name="$1" deadline=$((SECONDS + 2))
  while [ "$SECONDS" -lt "$deadline" ]; do
    if [ -s "$NPM_CALL_LOG" ]; then
      bad "$name" "$(cat "$NPM_CALL_LOG")"
      return 1
    fi
    sleep 0.2
  done
  ok "$name"
}

skills/worktree/tests/worktree_js_dependency_install.sh:61

  • wait_for_installs always returns success, even after timing out without observing any npm invocation. This makes failures harder to diagnose because callers can’t distinguish “npm didn’t run yet” from “npm never ran.” Consider returning non-zero on timeout and having callers surface a clearer failure message when the expected npm invocation hasn’t happened within the deadline.
wait_for_installs() {
  # The npm path is backgrounded; give it a moment to land.
  local deadline=$((SECONDS + 5))
  while [ "$SECONDS" -lt "$deadline" ]; do
    [ -s "$NPM_CALL_LOG" ] && return 0
    sleep 0.2
  done
  return 0
}

@bmethod
Brad (bmethod) added this pull request to the merge queue Aug 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0415610d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread skills/worktree/scripts/worktree Outdated
Comment thread CHANGELOG.md
@bmethod
Brad (bmethod) removed this pull request from the merge queue due to a manual request Aug 17, 2026
@github-actions

Copy link
Copy Markdown

Merge queue ejected this PR (removed_from_merge_queue, reason: MANUAL). The auto-merge arm is now DROPPED — nothing will merge this PR until someone re-arms it.

Ejecting merge-group run: not identified

Failing job(s):
(failing run not identified — the dequeue may predate run completion; check the queue insights page)

No same-named check comparison available for the PR head.

Automated by merge-queue-ejection-alert (VST-196). This alert never re-arms auto-merge.

Copilot AI review requested due to automatic review settings August 17, 2026 21:16
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 17, 2026 21:16

Dismissing prior approval to re-evaluate f9b37c7

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (4)

skills/worktree/tests/worktree_js_dependency_install.sh:85

  • The test reuses a single shared $NPM_CALL_LOG across multiple scenarios without clearing it between cases where assert_log_stays_empty is used. If a prior scenario logs unexpectedly, subsequent scenarios will fail immediately and can report earlier log lines, making failures harder to attribute to the specific case. Consider truncating : >\"$NPM_CALL_LOG\" before each scenario (right before invoking worktree create) so each assertion observes only the current run’s npm activity.
echo "=== a pnpm workspace never gets an npm install ==="
ROOT="$TMP_ROOT/pnpm"
make_repo "$ROOT" repo
printf '{ "name": "app", "devDependencies": {} }\n' >"$ROOT/repo/package.json"
printf 'lockfileVersion: "9.0"\n' >"$ROOT/repo/pnpm-lock.yaml"
git -C "$ROOT/repo" add package.json pnpm-lock.yaml
git -C "$ROOT/repo" commit -q -m "js: pnpm workspace"
git -C "$ROOT/repo" push -q origin main
(cd "$ROOT/repo" && "$WORKTREE_SCRIPT" create issue-pnpm >/dev/null)
assert_log_stays_empty "pnpm worktree skipped npm" || true

skills/worktree/tests/worktree_js_dependency_install.sh:90

  • The test reuses a single shared $NPM_CALL_LOG across multiple scenarios without clearing it between cases where assert_log_stays_empty is used. If a prior scenario logs unexpectedly, subsequent scenarios will fail immediately and can report earlier log lines, making failures harder to attribute to the specific case. Consider truncating : >\"$NPM_CALL_LOG\" before each scenario (right before invoking worktree create) so each assertion observes only the current run’s npm activity.
echo "=== a packageManager pin skips npm even without a lockfile ==="

skills/worktree/tests/worktree_js_dependency_install.sh:100

  • The test reuses a single shared $NPM_CALL_LOG across multiple scenarios without clearing it between cases where assert_log_stays_empty is used. If a prior scenario logs unexpectedly, subsequent scenarios will fail immediately and can report earlier log lines, making failures harder to attribute to the specific case. Consider truncating : >\"$NPM_CALL_LOG\" before each scenario (right before invoking worktree create) so each assertion observes only the current run’s npm activity.
(cd "$ROOT/repo" && "$WORKTREE_SCRIPT" create issue-pin >/dev/null)
assert_log_stays_empty "packageManager pin skipped npm" || true

echo "=== a pin formatted across lines is still a pin ==="

skills/worktree/tests/worktree_js_dependency_install.sh:108

  • The test reuses a single shared $NPM_CALL_LOG across multiple scenarios without clearing it between cases where assert_log_stays_empty is used. If a prior scenario logs unexpectedly, subsequent scenarios will fail immediately and can report earlier log lines, making failures harder to attribute to the specific case. Consider truncating : >\"$NPM_CALL_LOG\" before each scenario (right before invoking worktree create) so each assertion observes only the current run’s npm activity.
(cd "$ROOT/repo" && "$WORKTREE_SCRIPT" create issue-multiline >/dev/null)
assert_log_stays_empty "a multiline packageManager pin skips npm" || true

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9b37c70a9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread skills/worktree/scripts/worktree Outdated
Comment thread skills/worktree/scripts/worktree Outdated
Copilot AI review requested due to automatic review settings August 17, 2026 23:05
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 17, 2026 23:05

Dismissing prior approval to re-evaluate bc18fde

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

skills/worktree/tests/worktree_js_dependency_install.sh:61

  • wait_for_installs always returns success (even when no log entry appears), and several assertions rely on a fixed sleep 1 before grepping for a specific worktree name. This can make CI flaky on slower runners (backgrounded install/log write may land after the fixed sleep). Prefer a bounded poll that waits until either the expected marker appears in $NPM_CALL_LOG or a timeout is hit, and fail on timeout so the test outcome is deterministic.
wait_for_installs() {
  # The npm path is backgrounded; give it a moment to land.
  local deadline=$((SECONDS + 5))
  while [ "$SECONDS" -lt "$deadline" ]; do
    [ -s "$NPM_CALL_LOG" ] && return 0
    sleep 0.2
  done
  return 0
}

skills/worktree/scripts/worktree:1744

  • This function now combines (1) package-manager detection, (2) precedence rules, and (3) secure log-dir setup into one large block. To make future changes safer and easier to test/review, consider extracting small helpers (e.g., read_package_manager_pin, should_run_npm_install, npm_install_with_failure_log) and keeping install_worktree_dependencies as orchestration.
  [[ -f "$wt/package.json" ]] || return 0
  # npm is wrong for a workspace that pins another package manager: it
  # resolves a different tree and writes a stray package-lock.json into the
  # fresh checkout (dirty from birth, and `git add -A` would commit an npm
  # lockfile into a pnpm monorepo). Explicit npm evidence — its own lockfile
  # or an npm packageManager pin — wins over an incidental foreign lockfile;
  # otherwise another manager's lockfile or pin means that repo provisions
  # its own dependencies, so skip.
  # The TOP-LEVEL packageManager field only — a nested look-alike (e.g.
  # config.packageManager) is not a pin. Structural readers in preference
  # order: node (present wherever a package.json matters), then jq; a
  # machine with neither reads no pin, and the only cost of that miss is an
  # automatic install skipped — never a wrong-tree install.

Comment thread skills/worktree/scripts/worktree Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc18fde184

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread skills/worktree/scripts/worktree Outdated
…ign-evidence gate

npm tolerates a UTF-8 BOM, so the reader strips it before parsing; a
manifest the reader cannot parse reads no pin, and any foreign evidence
then skips the install regardless of a stale npm lockfile.

Claude-Session: https://claude.ai/code/session_01EwxSRv8oy1NxoQm66WKa4J
Copilot AI review requested due to automatic review settings August 17, 2026 23:12
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 17, 2026 23:12

Dismissing prior approval to re-evaluate 66c99d4

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Suppressed comments (3)

skills/worktree/tests/worktree_js_dependency_install.sh:124

  • This test (and a few similar ones) implicitly requires that the system running the tests has either node or jq available, because the worktree script’s pin detection uses those tools; if neither is present, the script treats the manifest as unreadable and will skip installs unless it sees unambiguous npm evidence, making these assertions fail in minimal CI images. Recommendation: add an explicit preflight at the top of the test to skip with a clear message when neither node nor jq is available, or adjust expectations in the specific cases where the behavior intentionally differs without a structural reader.
echo "=== a nested config.packageManager is not a pin ==="
ROOT="$TMP_ROOT/nested"
make_repo "$ROOT" repo
printf '{ "name": "app", "config": { "packageManager": "pnpm@10.33.2" } }\n' >"$ROOT/repo/package.json"
git -C "$ROOT/repo" add package.json
git -C "$ROOT/repo" commit -q -m "js: nested lookalike"
git -C "$ROOT/repo" push -q origin main
(cd "$ROOT/repo" && "$WORKTREE_SCRIPT" create issue-nested >/dev/null)
wait_for_installs
sleep 1
if grep -q "issue-nested" "$NPM_CALL_LOG" 2>/dev/null; then
  ok "a nested config.packageManager still gets the npm install"
else
  bad "nested lookalike not a pin" "log: $(cat "$NPM_CALL_LOG" 2>/dev/null)"
fi

CHANGELOG.md:18

  • The updated gating logic also treats pnpm-workspace.yaml as foreign evidence (skips npm even without a lockfile), and treats npm-shrinkwrap.json as explicit npm evidence. To avoid surprising users, it would help to mention the workspace-manifest case (and optionally shrinkwrap precedence) here so the changelog reflects the full set of signals the script uses.
- worktree: `create` installs npm dependencies only where npm is the
  package manager — a pnpm/yarn/bun lockfile or `packageManager` pin skips
  the step, so a fresh worktree in a pnpm workspace no longer starts dirty
  with a stray `package-lock.json`; a failed npm install names its log
  instead of vanishing (VST-340).

skills/worktree/scripts/worktree:1749

  • This node -e one-liner is doing several concerns at once (BOM stripping, parsing, top-level field selection, output normalization). It’s correct but difficult to audit/modify safely. Consider extracting this into a small helper function (still using node) written across multiple lines (with explicit error handling) so future changes to pin semantics are less error-prone.
    pkg_manager="$(node -e 'const m=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8").replace(/^\uFEFF/,"")).packageManager;process.stdout.write(typeof m==="string"?m:"")' "$wt/package.json" 2>/dev/null)" || pkg_manager_readable=0

Comment thread skills/worktree/scripts/worktree
Copilot AI review requested due to automatic review settings August 17, 2026 23:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66c99d4d6a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread skills/worktree/scripts/worktree

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (4)

skills/worktree/tests/worktree_js_dependency_install.sh:61

  • wait_for_installs returns success even if it times out without any npm invocation being logged, which can make the 'should install' assertions flaky (false negatives). Consider returning non-zero on timeout and letting the caller record a failure (or converting the helper into an assert_install_happened that fails with a useful message when the log stays empty).
wait_for_installs() {
  # The npm path is backgrounded; give it a moment to land.
  local deadline=$((SECONDS + 5))
  while [ "$SECONDS" -lt "$deadline" ]; do
    [ -s "$NPM_CALL_LOG" ] && return 0
    sleep 0.2
  done
  return 0
}

skills/worktree/scripts/worktree:1763

  • This only treats packageManager pins as authoritative when they include an @ (e.g. npm@...). If a repo uses \"packageManager\": \"npm\" (no version), it will fall through into the lockfile heuristics and may incorrectly skip the historical npm install when a foreign lockfile exists. To make behavior robust, handle the no-version forms too (e.g. npm|npm@*, and similarly for pnpm|pnpm@*, yarn|yarn@*, bun|bun@*).
  case "$pkg_manager" in
    pnpm@*|yarn@*|bun@*) return 0 ;;
    npm@*) ;;
    *)

skills/worktree/scripts/worktree:1763

  • The new explicit packageManager routing (especially the npm@* branch) is behaviorally important but isn't directly asserted in the added test script (current 'npm evidence wins' coverage is via lockfiles/shrinkwrap). Add a test case where packageManager is explicitly npm@... and a foreign lockfile (e.g. yarn.lock) is present, and assert that npm still runs.
  case "$pkg_manager" in
    pnpm@*|yarn@*|bun@*) return 0 ;;
    npm@*) ;;

skills/worktree/README.md:15

  • The doc says npm installs only when there's "no other manager's lockfile" (and no non-npm pin), but the implementation also treats pnpm-workspace.yaml as foreign evidence even without a lockfile. To keep the README accurate, include workspace manifests (at least pnpm-workspace.yaml) in the set of signals that suppress npm installs.
Worktrees are created under `<parent-of-checkout>/.worktrees/<checkout-name>/` — beside the checkout, not inside it, so editor file watchers never ingest worktree build outputs and sibling repos cannot collide. The default branch comes from `origin/HEAD` (fallback `main`). After creation the configured symlinks, copies, and scratch directories are applied. JS dependencies install automatically only where npm is the package manager (a `package.json` with no other manager's lockfile and no non-npm `packageManager` pin; explicit npm evidence wins over an incidental foreign lockfile) — pnpm/yarn/bun workspaces provision their own dependencies. The install runs in the background with every std fd detached (a `$(create)` capture returns at once); a failure leaves its full log under the user-private `${TMPDIR:-/tmp}/worktree-npm-install-<uid>/` dir, keyed by worktree name and path checksum.

@bmethod
Brad (bmethod) added this pull request to the merge queue Aug 17, 2026
Merged via the queue into main with commit 78dbf8e Aug 17, 2026
38 checks passed
@bmethod
Brad (bmethod) deleted the vst-340 branch August 17, 2026 23:32
Brad (bmethod) added a commit that referenced this pull request Aug 18, 2026
Completes the PR #1451 review follow-up by deleting the shared-/tmp attack
surface outright. The predictable `${TMPDIR:-/tmp}/worktree-npm-install-<uid>`
scheme was raceable on a shared machine: another local user could pre-plant a
symlink on the log file, and the directory's -L/-d/-O/chmod check sequence was
non-atomic no matter how it was ordered.

The log now lands at `<gitdir>/npm-install.log`. Each linked worktree has its
own gitdir under the main checkout's `.git/worktrees/<name>/`, which is
per-worktree and owner-controlled by construction — no shared directory, no
predictable leaf, and no hardening dance to get wrong. An unresolvable or
unwritable gitdir runs the install unlogged rather than failing, as before; the
detached std fds, the flags, and the delete-on-success behaviour are unchanged.

Two pins added: a failed install leaves its full output at that path, and a
clean one leaves nothing behind.

Claude-Session: https://claude.ai/code/session_01EwxSRv8oy1NxoQm66WKa4J
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 create runs npm install in pnpm workspaces, leaving a stray package-lock.json

2 participants