feat: add an option to skip copying local files #9

Merged
jercik merged 2 commits from feat/no-copy into main 2026-10-24 13:00:59 +00:00
Owner

--no-copy creates a worktree without bringing over untracked or ignored local files, including local credentials and fixtures. Combine it with --no-setup when only the checkout is needed.

Copying stays enabled by default. Keeping the two flags independent preserves the published meaning of --no-setup; making that flag disable copying would change existing invocations.

`--no-copy` creates a worktree without bringing over untracked or ignored local files, including local credentials and fixtures. Combine it with `--no-setup` when only the checkout is needed. Copying stays enabled by default. Keeping the two flags independent preserves the published meaning of `--no-setup`; making that flag disable copying would change existing invocations.
feat: add an option to skip copying local files
Some checks failed
Checks / pr-flow-canary (pull_request) Has been skipped
Checks / reuse (pull_request) Successful in 5s
commit-msg / commitlint (pull_request) Successful in 9s
Checks / quality-checks (26.5.0) (pull_request) Failing after 37s
Checks / quality-checks (24.15.0) (pull_request) Failing after 37s
Review / Review (pull_request_target) Has been cancelled
ba8dcdd297
test: reset queued setup responses between worktree tests
All checks were successful
Checks / pr-flow-canary (pull_request) Has been skipped
Checks / reuse (pull_request) Successful in 5s
commit-msg / commitlint (pull_request) Successful in 9s
Checks / quality-checks (24.15.0) (pull_request) Successful in 56s
Checks / quality-checks (26.5.0) (pull_request) Successful in 1m0s
Review / Review (pull_request_target) Successful in 5m35s
1190ca21c4

Review 01M5PZB9J3Y5W4B965DYFN0S4D — head 1190ca21c43d96c428cb169d8576bd6179314b9c

Review — j4k-oss/worktree-add @ e4531694e0

Scope: diff against base tree d4285197bfe0
Review finished: 10 succeeded, 0 failed
Triage: settled
Facts: current review-wide projection

Computed under:

{
  "abandonment": "abandonment-v1",
  "anchor_recipe": 1,
  "batch_policy": "batch-v1",
  "coverage": "coverage-v3",
  "dispatch_policy": "dispatch-v2",
  "grounder_version": 1,
  "grounding_read_rule": "grounding-read-v1",
  "promotion_policy": "promotion-v1",
  "report": "report-v5",
  "tally": "tally-v1",
  "triage_settle": "triage-settle-v2"
}

Findings (1)

low — Reporting a skipped stage is now copied per stage in runWorktreeAdd, and each copy goes around the status logger

  • claim: 01M5PZHQSSFN5XBSXF0P4KMM7R
  • anchor: src/cli/run-worktree-add.ts (snippet)
    } else if (dryRun) {
      logger.step("Would skip copying untracked and ignored local files (--no-copy).");
    } else {
      console.error("Skipping copying untracked and ignored local files (--no-copy).");
    }
  • lens: aposd-plain · arm: default
  • verdicts: 1 valid / 0 invalid / 0 uncertain
    • pass 01M5PZJERVSFNSVW19PC8R649Y · valid: Grounded excerpt (exact) shows the new --no-copy skip triad. This claim's body and the companion module-design claim in this batch both describe the --no-setup block with the same shape, and the companion quotes its arms. That establishes the APoSD Repetition red flag: a nontrivial three-arm branch now appears twice in runWorktreeAdd. It also supports principle 10 (pull complexity downward): dry-run wording and the choice of output channel are decided at each skip site instead of in one helper or logger operation. The point about the missing '➤ ' decoration is the reviewer's static reasoning, and it is coherent: a raw console.error cannot pick up the logger's decorate prefix. It adds an inconsistency argument but is not essential to the finding. 'Repeated over and over' overstates two copies, and the visibility rule is partly the logger's legitimate public interface (step is verbose-only by design). Neither objection removes the supported repetition and the missing single home. Both remedies, a logger skipped() method or a local helper, fix it while preserving the pinned output. Low severity fits. This agrees with the earlier valid verdict on prior claim 01M5PZCB8VTNFMHJSNPY1Y4GVW. Nothing in this batch changes that assessment.
  • duplicates: 01M5PZHRQB4NESJGFH78SDXS0S (module-design)
  • disposition: none

The --no-copy branch copies the three-arm --no-setup block line for line: run the stage, else logger.step("Would skip …") under dry run, else a raw console.error("Skipping …"). So runWorktreeAdd now holds two copies of a rule that belongs to the logger: step is hidden unless verbose || dryRun (createStatusLogger), so an always-visible skip notice has to go around the logger. Each skippable stage has to know that rule again. The bypass also skips the logger's formatting. With --verbose on a TTY, the stage messages around it start with ➤ , but "Skipping copying untracked and ignored local files (--no-copy)." prints bare. The dry-run and real-run wording is also kept as two hand-written strings per stage ("Would skip copying …" / "Skipping copying …").

Fix: let the logger own skip notices. One option is a StatusLogger method such as skipped(message) that always prints and adds the dry-run prefix and decoration, so each stage becomes if (options.copy !== false) { await copyUntrackedFiles(…) } else { logger.skipped("copying untracked and ignored local files (--no-copy)") }. Another is a local helper in runWorktreeAdd that takes the flag, the stage thunk and the description. Either way the visibility rule and the message shape live in one place, and the copy and setup branches share it.

I read src/cli/run-worktree-add.ts (the new copy branch and the existing setup branch right after it) and src/output/create-status-logger.ts (step returns early when shouldLogSteps is false; warn is the only level that always prints). The new test in src/cli/run-worktree-add.test.ts pins the dry-run text "DRY RUN: Would skip copying untracked and ignored local files (--no-copy).", which comes from logger.step adding dryRunPrefix. The missing ➤ in verbose decorated output is static reasoning: decorate depends on process.stderr.isTTY, and no test runs that path. If skip notices are meant to stay undecorated and outside the logger on purpose, a comment saying so would settle it. The code has none.

Other claims

  • grounding-pending (0)
  • ungrounded (0)
  • rejected (0)
  • duplicate-of (1)
    • 01M5PZHRQB4NESJGFH78SDXS0S low — --no-copy copies the --no-setup skip-notice branch, so the rule for announcing a skipped step now lives in two places → 01M5PZHQSSFN5XBSXF0P4KMM7R
  • unadjudicated (0)

Coverage

Coverage pass: 01M5PZBN3N4W85FF8KHY8DGSXG
Accounting: complete (all slots terminal; see slot results for failures)
Slot health: healthy

lens part arm unit status runs loss
general-bug whole default no-claims 1 no
writing-quality whole default no-claims 1 no
test-trimming whole default no-claims 1 no
restated-sets whole default no-claims 1 no
project-docs whole default no-claims 1 no
unix-cli whole default no-claims 1 no
use-effect whole default no-claims 1 no
module-design whole default claims-emitted 1 no
aposd-plain whole default claims-emitted 1 no
react whole default no-claims 1 no
<!-- review:summary --> **Review** `01M5PZB9J3Y5W4B965DYFN0S4D` — head `1190ca21c43d96c428cb169d8576bd6179314b9c` # Review — j4k-oss/worktree-add @ e4531694e078 Scope: diff against base tree `d4285197bfe0` Review finished: 10 succeeded, 0 failed Triage: settled Facts: current review-wide projection Computed under: ```json { "abandonment": "abandonment-v1", "anchor_recipe": 1, "batch_policy": "batch-v1", "coverage": "coverage-v3", "dispatch_policy": "dispatch-v2", "grounder_version": 1, "grounding_read_rule": "grounding-read-v1", "promotion_policy": "promotion-v1", "report": "report-v5", "tally": "tally-v1", "triage_settle": "triage-settle-v2" } ``` ## Findings (1) ### low — Reporting a skipped stage is now copied per stage in `runWorktreeAdd`, and each copy goes around the status logger - claim: `01M5PZHQSSFN5XBSXF0P4KMM7R` - anchor: `src/cli/run-worktree-add.ts` (snippet) ``` } else if (dryRun) { logger.step("Would skip copying untracked and ignored local files (--no-copy)."); } else { console.error("Skipping copying untracked and ignored local files (--no-copy)."); } ``` - lens: aposd-plain · arm: default - verdicts: 1 valid / 0 invalid / 0 uncertain - pass `01M5PZJERVSFNSVW19PC8R649Y` · valid: Grounded excerpt (exact) shows the new --no-copy skip triad. This claim's body and the companion module-design claim in this batch both describe the --no-setup block with the same shape, and the companion quotes its arms. That establishes the APoSD Repetition red flag: a nontrivial three-arm branch now appears twice in runWorktreeAdd. It also supports principle 10 (pull complexity downward): dry-run wording and the choice of output channel are decided at each skip site instead of in one helper or logger operation. The point about the missing '➤ ' decoration is the reviewer's static reasoning, and it is coherent: a raw console.error cannot pick up the logger's decorate prefix. It adds an inconsistency argument but is not essential to the finding. 'Repeated over and over' overstates two copies, and the visibility rule is partly the logger's legitimate public interface (step is verbose-only by design). Neither objection removes the supported repetition and the missing single home. Both remedies, a logger skipped() method or a local helper, fix it while preserving the pinned output. Low severity fits. This agrees with the earlier valid verdict on prior claim 01M5PZCB8VTNFMHJSNPY1Y4GVW. Nothing in this batch changes that assessment. - duplicates: `01M5PZHRQB4NESJGFH78SDXS0S` (module-design) - disposition: none > The `--no-copy` branch copies the three-arm `--no-setup` block line for line: run the stage, else `logger.step("Would skip …")` under dry run, else a raw `console.error("Skipping …")`. So `runWorktreeAdd` now holds two copies of a rule that belongs to the logger: `step` is hidden unless `verbose || dryRun` (`createStatusLogger`), so an always-visible skip notice has to go around the logger. Each skippable stage has to know that rule again. The bypass also skips the logger's formatting. With `--verbose` on a TTY, the stage messages around it start with `➤ `, but "Skipping copying untracked and ignored local files (--no-copy)." prints bare. The dry-run and real-run wording is also kept as two hand-written strings per stage ("Would skip copying …" / "Skipping copying …"). > > Fix: let the logger own skip notices. One option is a `StatusLogger` method such as `skipped(message)` that always prints and adds the dry-run prefix and decoration, so each stage becomes `if (options.copy !== false) { await copyUntrackedFiles(…) } else { logger.skipped("copying untracked and ignored local files (--no-copy)") }`. Another is a local helper in `runWorktreeAdd` that takes the flag, the stage thunk and the description. Either way the visibility rule and the message shape live in one place, and the copy and setup branches share it. > > I read `src/cli/run-worktree-add.ts` (the new copy branch and the existing setup branch right after it) and `src/output/create-status-logger.ts` (`step` returns early when `shouldLogSteps` is false; `warn` is the only level that always prints). The new test in `src/cli/run-worktree-add.test.ts` pins the dry-run text "DRY RUN: Would skip copying untracked and ignored local files (--no-copy).", which comes from `logger.step` adding `dryRunPrefix`. The missing `➤ ` in verbose decorated output is static reasoning: `decorate` depends on `process.stderr.isTTY`, and no test runs that path. If skip notices are meant to stay undecorated and outside the logger on purpose, a comment saying so would settle it. The code has none. ## Other claims - grounding-pending (0) - ungrounded (0) - rejected (0) - duplicate-of (1) - `01M5PZHRQB4NESJGFH78SDXS0S` low — `--no-copy` copies the `--no-setup` skip-notice branch, so the rule for announcing a skipped step now lives in two places → `01M5PZHQSSFN5XBSXF0P4KMM7R` - unadjudicated (0) ## Coverage Coverage pass: 01M5PZBN3N4W85FF8KHY8DGSXG Accounting: complete (all slots terminal; see slot results for failures) Slot health: healthy | lens | part | arm | unit status | runs | loss | | --- | --- | --- | --- | --- | --- | | general-bug | whole | default | no-claims | 1 | no | | writing-quality | whole | default | no-claims | 1 | no | | test-trimming | whole | default | no-claims | 1 | no | | restated-sets | whole | default | no-claims | 1 | no | | project-docs | whole | default | no-claims | 1 | no | | unix-cli | whole | default | no-claims | 1 | no | | use-effect | whole | default | no-claims | 1 | no | | module-design | whole | default | claims-emitted | 1 | no | | aposd-plain | whole | default | claims-emitted | 1 | no | | react | whole | default | no-claims | 1 | no |
Lines 148-152
@ -146,0 +145,8 @@
dryRun,
logger,
});
} else if (dryRun) {
logger.step("Would skip copying untracked and ignored local files (--no-copy).");
} else {
console.error("Skipping copying untracked and ignored local files (--no-copy).");
}

low — Reporting a skipped stage is now copied per stage in runWorktreeAdd, and each copy goes around the status logger

The --no-copy branch copies the three-arm --no-setup block line for line: run the stage, else logger.step("Would skip …") under dry run, else a raw console.error("Skipping …"). So runWorktreeAdd now holds two copies of a rule that belongs to the logger: step is hidden unless verbose || dryRun (createStatusLogger), so an always-visible skip notice has to go around the logger. Each skippable stage has to know that rule again. The bypass also skips the logger's formatting. With --verbose on a TTY, the stage messages around it start with ➤ , but "Skipping copying untracked and ignored local files (--no-copy)." prints bare. The dry-run and real-run wording is also kept as two hand-written strings per stage ("Would skip copying …" / "Skipping copying …").

Fix: let the logger own skip notices. One option is a StatusLogger method such as skipped(message) that always prints and adds the dry-run prefix and decoration, so each stage becomes if (options.copy !== false) { await copyUntrackedFiles(…) } else { logger.skipped("copying untracked and ignored local files (--no-copy)") }. Another is a local helper in runWorktreeAdd that takes the flag, the stage thunk and the description. Either way the visibility rule and the message shape live in one place, and the copy and setup branches share it.

I read src/cli/run-worktree-add.ts (the new copy branch and the existing setup branch right after it) and src/output/create-status-logger.ts (step returns early when shouldLogSteps is false; warn is the only level that always prints). The new test in src/cli/run-worktree-add.test.ts pins the dry-run text "DRY RUN: Would skip copying untracked and ignored local files (--no-copy).", which comes from logger.step adding dryRunPrefix. The missing ➤ in verbose decorated output is static reasoning: decorate depends on process.stderr.isTTY, and no test runs that path. If skip notices are meant to stay undecorated and outside the logger on purpose, a comment saying so would settle it. The code has none.

lens aposd-plain · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M5PZHQSSFN5XBSXF0P4KMM7R of review 01M5PZB9J3Y5W4B965DYFN0S4D

<!-- review:claim:01M5PZHQSSFN5XBSXF0P4KMM7R --> **low** — Reporting a skipped stage is now copied per stage in `runWorktreeAdd`, and each copy goes around the status logger > The `--no-copy` branch copies the three-arm `--no-setup` block line for line: run the stage, else `logger.step("Would skip …")` under dry run, else a raw `console.error("Skipping …")`. So `runWorktreeAdd` now holds two copies of a rule that belongs to the logger: `step` is hidden unless `verbose || dryRun` (`createStatusLogger`), so an always-visible skip notice has to go around the logger. Each skippable stage has to know that rule again. The bypass also skips the logger's formatting. With `--verbose` on a TTY, the stage messages around it start with `➤ `, but "Skipping copying untracked and ignored local files (--no-copy)." prints bare. The dry-run and real-run wording is also kept as two hand-written strings per stage ("Would skip copying …" / "Skipping copying …"). > > Fix: let the logger own skip notices. One option is a `StatusLogger` method such as `skipped(message)` that always prints and adds the dry-run prefix and decoration, so each stage becomes `if (options.copy !== false) { await copyUntrackedFiles(…) } else { logger.skipped("copying untracked and ignored local files (--no-copy)") }`. Another is a local helper in `runWorktreeAdd` that takes the flag, the stage thunk and the description. Either way the visibility rule and the message shape live in one place, and the copy and setup branches share it. > > I read `src/cli/run-worktree-add.ts` (the new copy branch and the existing setup branch right after it) and `src/output/create-status-logger.ts` (`step` returns early when `shouldLogSteps` is false; `warn` is the only level that always prints). The new test in `src/cli/run-worktree-add.test.ts` pins the dry-run text "DRY RUN: Would skip copying untracked and ignored local files (--no-copy).", which comes from `logger.step` adding `dryRunPrefix`. The missing `➤ ` in verbose decorated output is static reasoning: `decorate` depends on `process.stderr.isTTY`, and no test runs that path. If skip notices are meant to stay undecorated and outside the logger on purpose, a comment saying so would settle it. The code has none. lens `aposd-plain` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M5PZHQSSFN5XBSXF0P4KMM7R` of review `01M5PZB9J3Y5W4B965DYFN0S4D`
jercik marked this conversation as resolved
Author
Owner

Replying to review comment #186410

The two skip-message branches share a shape, and normal skip notices follow the existing --no-setup behavior: always-visible stderr text without the verbose step decoration. Dry-run notices remain truthful. This bounded change adds an independent copy toggle; the session scope explicitly prioritizes the simple implementation and excludes optional logger/shared-stage abstractions. The suggested extraction is therefore declined for this task, with no follow-up. No correctness defect was found, and this is not claimed fixed.

Reviewed head: 1190ca21c4.

> Replying to review comment #186410 The two skip-message branches share a shape, and normal skip notices follow the existing --no-setup behavior: always-visible stderr text without the verbose step decoration. Dry-run notices remain truthful. This bounded change adds an independent copy toggle; the session scope explicitly prioritizes the simple implementation and excludes optional logger/shared-stage abstractions. The suggested extraction is therefore declined for this task, with no follow-up. No correctness defect was found, and this is not claimed fixed. Reviewed head: 1190ca21c43d96c428cb169d8576bd6179314b9c.
jercik merged commit 9a9070d2df into main 2026-10-24 13:00:59 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
j4k-oss/worktree-add!9
No description provided.