Skip to content

fix(RUFU-018): add noCommitsExpected dep-sync skip and corepack/pnpm env passthrough - #2501

Merged
gsxdsm merged 5 commits into
Runfusion:mainfrom
ischindl:rufu-018-clean-pr
Jul 31, 2026
Merged

fix(RUFU-018): add noCommitsExpected dep-sync skip and corepack/pnpm env passthrough#2501
gsxdsm merged 5 commits into
Runfusion:mainfrom
ischindl:rufu-018-clean-pr

Conversation

@ischindl

@ischindl ischindl commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Manually land RUFU-018 fix bypassing the AI merge pipeline.

Summary

  • Add noCommitsExpected flag to LandRepoContext; skip dependency sync when set
  • Forward COREPACK_HOME/PNPM_HOME/npm_config_registry in installWorktreeDependencies
  • Add comprehensive tests for both changes

This unblocks all downstream RUFU audit tasks.

Surface Enumeration

  • Providers/bridges: installWorktreeDependencies called from landOneRepo (AI merge) and legacy merger.ts; landOneRepo called from runAiMerge and landWorkspaceTask
  • Data states: noCommitsExpected can be true, false, or undefined — both callers use === true strict check

Summary by CodeRabbit

  • New Features

    • Improved support for tasks that do not produce commits by skipping unnecessary dependency installation during merges.
    • Preserved normal merge and review behavior when dependency installation is skipped.
  • Bug Fixes

    • Dependency installation now correctly preserves relevant package-manager and system environment settings.
    • Reduced installation failures caused by missing or unavailable package-manager configuration.

ischindl and others added 4 commits July 28, 2026 22:22
…ntext and skip dep sync

Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: RUFU-018
…n installWorktreeDependencies

Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: RUFU-018
Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: RUFU-018
Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: RUFU-018
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Dependency installation now preserves selected environment variables for pnpm child processes. AI-merge landing propagates noCommitsExpected and skips clean-room dependency synchronization for those tasks, with tests covering both behaviors.

Changes

Clean-room dependency flow

Layer / File(s) Summary
Dependency installation environment passthrough
packages/engine/src/merge-dependency-sync.ts, packages/engine/src/__tests__/merge-dependency-sync-lockfile-heal.test.ts
installWorktreeDependencies forwards selected environment variables while preserving the existing process environment, with child-process coverage.
No-commit dependency synchronization skip
packages/engine/src/merger-ai.ts
noCommitsExpected is added to the landing context, propagated by both orchestrators, and used by landOneRepo to skip dependency synchronization.
Landing and environment behavior validation
packages/engine/src/__tests__/merger-ai-no-commits-deps-skip.test.ts
Git-backed tests verify successful landing and dependency-sync invocation for true, false, and omitted flag values.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant runAiMerge
  participant landWorkspaceTask
  participant landOneRepo
  participant installWorktreeDependencies
  runAiMerge->>landOneRepo: pass noCommitsExpected
  landWorkspaceTask->>landOneRepo: pass noCommitsExpected
  alt noCommitsExpected is true
    landOneRepo->>landOneRepo: skip dependency synchronization
  else flag is false or unset
    landOneRepo->>installWorktreeDependencies: synchronize dependencies
  end
Loading

Possibly related PRs

  • Runfusion/Fusion#2438: Implements the same noCommitsExpected and environment passthrough changes in overlapping code paths.
  • Runfusion/Fusion#2437: Directly overlaps the dependency-sync skip, environment forwarding, and related tests.
  • Runfusion/Fusion#1920: Also changes landOneRepo to avoid dependency synchronization in an early landing path.

Suggested reviewers: gsxdsm

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: skipping dependency sync for noCommitsExpected and passing through corepack/pnpm-related env vars.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread packages/engine/src/merger-ai.ts
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds task-directed dependency-sync skipping and dependency-installer environment propagation.

  • Threads noCommitsExpected through single-repository and workspace AI-merge paths.
  • Passes the engine environment explicitly to dependency-install subprocesses.
  • Adds coverage for the skip states, environment propagation, and absent environment variables.

Confidence Score: 3/5

The PR should not merge until dependency synchronization is skipped only after confirming that the branch has no relevant changes.

The new branch trusts mutable expectation metadata rather than repository state, and the added test confirms that actual code changes can be landed without dependency installation or lockfile validation.

Files Needing Attention: packages/engine/src/merger-ai.ts and packages/engine/src/tests/merger-ai-no-commits-deps-skip.test.ts

Important Files Changed

Filename Overview
packages/engine/src/merger-ai.ts Adds the noCommitsExpected skip and caller propagation, but trusts expectation metadata even when the branch has real changes.
packages/engine/src/merge-dependency-sync.ts Supplies an explicit process-environment snapshot to dependency-install subprocesses; no independently actionable defect was found.
packages/engine/src/tests/merger-ai-no-commits-deps-skip.test.ts Covers true, false, and undefined states, while also confirming the unsafe true-with-actual-changes behavior.
packages/engine/src/tests/merge-dependency-sync-lockfile-heal.test.ts Adds focused fake-pnpm coverage for propagated, preserved, and undefined environment variables.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Task[Task metadata] --> Flag{noCommitsExpected true?}
  Flag -- Yes --> Skip[Skip dependency synchronization]
  Flag -- No --> Install[Install and validate dependencies]
  Skip --> Merge[Merge and review]
  Install --> Merge
  Merge --> Land[Land squash result]
Loading

Reviews (1): Last reviewed commit: "test(RUFU-018): complete Step 4 — add en..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/engine/src/__tests__/merge-dependency-sync-lockfile-heal.test.ts`:
- Around line 171-200: Update the cleanup logic in the test cases around
installWorktreeDependencies to restore environment variables by deleting each
key when its saved original value is undefined; otherwise assign the saved
value. Apply this to COREPACK_HOME, PNPM_HOME, and npm_config_registry in both
affected finally blocks so absent variables do not become the literal string
"undefined".

In `@packages/engine/src/__tests__/merger-ai-no-commits-deps-skip.test.ts`:
- Around line 137-260: Add targeted regression tests that invoke both task-entry
paths, runAiMerge and landWorkspaceTask, with task.noCommitsExpected set to
true. Assert installWorktreeDependencies is not called through each entry point,
while preserving successful merge behavior; keep the existing direct
LandRepoContext coverage unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 350a47dd-a5c0-46a9-9161-dde9b237ff35

📥 Commits

Reviewing files that changed from the base of the PR and between 2934ccc and c632c11.

📒 Files selected for processing (4)
  • packages/engine/src/__tests__/merge-dependency-sync-lockfile-heal.test.ts
  • packages/engine/src/__tests__/merger-ai-no-commits-deps-skip.test.ts
  • packages/engine/src/merge-dependency-sync.ts
  • packages/engine/src/merger-ai.ts

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Checking thread status for a hand-merge (this PR is cleared but has 3 live threads). Two are coderabbit maintainability notes. The greptile P1 is confirmed by this PR's own test, so it is worth a decision rather than a dismissal.

What the P1 says

When a task remains marked noCommitsExpected but its branch contains source, manifest, or lockfile changes, this branch skips dependency installation and frozen-lockfile validation, allowing build-breaking dependency changes or an inconsistent lockfile to land.

The test in this PR encodes exactly that

it("skips installWorktreeDependencies entirely when noCommitsExpected: true", async () => {
  fx = createRepoFixture(true); // branch has actual changes
  ...
  noCommitsExpected: true,
  ...
  expect(vi.mocked(installWorktreeDependencies)).not.toHaveBeenCalled();
  expect(result.outcome).toBe("landed");
});

The fixture is deliberately constructed as true"branch has actual changes" — the flag claims no commits, and the assertion is that dep sync is skipped and the land still succeeds. That is the bypass greptile describes, written down as the intended contract.

So the question is intent, not behaviour

noCommitsExpected is either a trusted assertion from the caller (flag wins, current behaviour, and the P1 is a wontfix to be resolved with a note) or a hint that should be verified against the branch before skipping validation (in which case the skip needs a guard and that test needs inverting).

I am not deciding that — I did not write this PR and the trade depends on where noCommitsExpected originates and how much it is trusted elsewhere. But the P1 is not bot noise: it describes real, deliberate behaviour, and merging by hand resolves it silently.

For completeness, thread status across the three PRs cleared for hand-merge: #2376 is clear (0 unresolved), this one has 3, and #2377 has 2 coderabbit "Major" threads (fork-aware PR path coverage; a functional-correctness note on task-lifecycle.ts).

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Worked all three threads. The P1 is real; fixes are pushed to origin/rufu-018-clean-pr at 2972472dbb (this PR's head is on a fork, ischindl:rufu-018-clean-pr, which I cannot push to — the branch can be merged directly or the PR retargeted).


1. greptile P1, merger-ai.ts — metadata bypasses dependency validation → CONFIRMED, fixed

Not hypothetical, and stronger than the comment states: the skip only ever runs on a branch that has commits. The rev-list --count short-circuit ~50 lines above returns outcome: "empty" when the branch is zero ahead, so control reaches the dep-sync step only when it is AHEAD.

And nothing revalidates the flag. Both downstream empty-lane guards explicitly carve out no-commits tasks:

Both protect the opposite direction (commit-expected task, empty branch). The inverse — no-commits task, non-empty branch — had no check at all. A task marked no-commits whose executor committed a lockfile change got its install and its frozen-lockfile validation skipped.

Fix: the flag now says look; the branch diff decides. If main...branch touches package.json or any LOCKFILE_CANDIDATES entry, it falls through to the normal sync and emits an audit row with skipOverridden: true. An unreadable diff also syncs — matching the hard-fail contract documented directly above that block, rather than assuming absence of evidence is evidence of safety.

LOCKFILE_CANDIDATES is now exported from merge-dependency-sync.ts instead of duplicated, so the skip and the installer cannot drift on what counts as a dependency change.

Regression added, and mutation-verified: reverting to trust-the-flag fails exactly the new case and nothing else. Your existing "lands successfully with noCommitsExpected: true and actual changes" case is untouched and still passes — feature.txt is not a dependency file, so an ordinary source change on a no-commits task still skips. The new case differs only in which file the branch touches, which is the whole point.

One note on my own work: my first version of that test committed the lockfile while the fixture had left the tree on main, so the main...branch diff could not see it and the case passed for the wrong reason. Corrected, with the reason in a comment.


2. coderabbit, lockfile-heal.test.ts:200 — restore absent env vars by deleting → CORRECT, fixed

process.env.X = undefined stores the string "undefined", so a var absent before the test is left truthy afterwards and leaks into every later test in the process. Added a restoreEnv helper and applied it at both sites (197-199 and 256-258). 10/10 green.


3. coderabbit, both task-entry propagation paths → valid, deferring as follow-up

The observation is right: these tests construct LandRepoContext directly, so a broken handoff from task.noCommitsExpected in runAiMerge or landWorkspaceTask would not be caught.

Deferring rather than blocking, deliberately. Both entry points sit behind real worktrees, sessions and a merge agent; a regression through them needs a harness none of these tests have, and the cheap version — asserting the field is read at the call site — is a mirrored-implementation test that cannot fail on a revert. This repo has already deleted two of those (analytics-timing-roles-resolved, and the four cases removed from executor-stale-spec-active-lanes) for exactly that reason.

What the P1 fix does change: the propagation path is no longer the only thing standing between a stale flag and an unvalidated lockfile. Even if the handoff broke open (flag wrongly true), the diff gate now catches the dangerous case.


Verification on 2972472dbb: merger-ai-no-commits-deps-skip 5/5, merge-dependency-sync-lockfile-heal 10/10, engine typecheck clean, pnpm lint clean.

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Do not merge this as-is — the review fixes are not in it.

This PR's head is the fork branch ischindl:rufu-018-clean-pr, which I cannot push to. The three threads are resolved because I judged and fixed them, but the commits live on origin/rufu-018-clean-pr, not here. Merging #2501 ships the greptile P1 unfixed — a no-commits task whose branch changes a lockfile still skips install and frozen-lockfile validation.

#2958 supersedes this: your five commits unmodified, plus the review fixes. One PR in, one out — no queue growth. Close this when #2958 lands.

Full reasoning is in the comment above and in #2958's body.

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Superseded by #2958 — same four files, same branch name, opened from upstream

#2958 (Runfusion/Fusion:rufu-018-clean-pr, opened 2026-07-31) changes exactly the same four files as this one (ischindl/Fusion:rufu-018-clean-pr, 2026-07-28):

packages/engine/src/__tests__/merge-dependency-sync-lockfile-heal.test.ts
packages/engine/src/__tests__/merger-ai-no-commits-deps-skip.test.ts
packages/engine/src/merge-dependency-sync.ts
packages/engine/src/merger-ai.ts

They read as the same change re-landed from the upstream repo rather than the fork (+542/-2 vs +425/-0 — the newer one has grown, so it is not a straight copy). Both are open and both are mergeable, so whichever merges second will conflict or silently re-apply.

Flagging rather than closing: I did not author either, and if #2958 dropped something this one has, that is worth catching before this is closed. Owner's call — if #2958 carries everything, close this as superseded.

Worth noting the fork angle specifically, since it is easy to miss in a PR list: these are distinguishable only by head.repo.full_name, not by branch name. The list shows two rows with the identical rufu-018-clean-pr head.

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

This and #2958 are the same branch, and #2958 says it supersedes this one — so this PR is counted twice in the queue.

Measured:

PR #2501 PR #2958
head rufu-018-clean-pr rufu-018-clean-pr
base main main
recorded head OID e3295b1d9 2972472db
state OPEN OPEN

git ls-remote origin rufu-018-clean-pr2972472db, and e3295b1d9 is an ancestor of it. So the branch moved forward under both PRs; this one is showing an outdated view of the same commits #2958 now describes, including the review P1 that PR fixed.

It does resolve itself on merge — merging #2958 lands this exact branch into main, which auto-closes this PR. So nothing is broken, and there is no risk of divergent content.

Flagging it anyway for two reasons. It inflates the open count by one while the queue is being drawn down, and two reviewers can independently pick up the same branch from different PRs and duplicate the review — the consolidation protocol asks for the superseded PR to be closed with a superseded by #<n> note precisely to stop that.

Not closing it — your PR, your call, and closing the wrong one of a matched pair is the kind of thing worth leaving to whoever knows the intent.

@gsxdsm

gsxdsm commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Do not hand-merge this one — it points at a stale commit and would land the version WITHOUT the review fix.

#2501 and #2958 are two PRs over the same branch (rufu-018-clean-pr), but they reference different heads:

#2501 head: e3295b1d960e
#2958 head: 2972472dbb14   ← branch tip

e3295b1d960e is an ancestor of the tip, and the one commit between them is exactly the review fix:

fix(merger-ai): gate the no-commits dep-sync skip on the branch diff (PR #2501 review)
greptile P1: the skip trusted ctx.noCommitsExpected alone… A no-commits task whose executor committed a lockfile change had its install and its frozen-lockfile validation skipped.

Both PRs show all four checks green, which is what makes this dangerous: #2501 looks ready by every signal on the page, and merging it lands the code the P1 was raised against. Squash-merging #2501 would take its recorded head, not the branch tip.

Merge #2958 instead and close this, or re-point/refresh #2501 so it picks up 2972472dbb14.

Flagging rather than acting: closing a PR that was explicitly cleared for merge is your call, not mine. I have not touched either branch.

@gsxdsm
gsxdsm merged commit 8d6acf1 into Runfusion:main Jul 31, 2026
6 checks passed
gsxdsm added a commit that referenced this pull request Jul 31, 2026
…(PR #2501 review)

greptile P1: the skip trusted ctx.noCommitsExpected alone. Control only reaches
that line when the branch is AHEAD — the rev-list short-circuit returns early at
zero — and both downstream empty-lane guards explicitly carve out
noCommitsExpected tasks, so nothing revalidates the expectation against what
actually landed. A no-commits task whose executor committed a lockfile change
had its install and its frozen-lockfile validation skipped.

The flag now says look; the diff decides. A branch touching package.json or any
LOCKFILE_CANDIDATES entry falls through to the normal sync and is audited. An
unreadable diff also syncs, matching the hard-fail contract above it.

LOCKFILE_CANDIDATES is exported rather than duplicated so the skip and the
installer cannot drift on what counts as a dependency change.

Also fixes the coderabbit env nit: restoring an absent var by assignment stores
the string "undefined" and leaks into later tests; delete it instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gsxdsm added a commit that referenced this pull request Jul 31, 2026
…— the P1 #2501 shipped without (#2958)

## #2501 merged without its P1 fix; this is that fix, alone

#2501 has landed. Its review threads were resolved — I judged and fixed
them — but its head was a fork branch I could not push to, so **the
fixes were never in it**. Confirmed on `main` at `c1c1b964af`:

```
merger-ai.ts:902:      if (ctx.noCommitsExpected === true) {      ← bare flag, no diff gate
merge-dependency-sync.ts: export const LOCKFILE_CANDIDATES → 0 matches
```

Rebasing dropped this PR's five duplicated base commits, so it is now
**one commit**: the review fix and its regression.

## The defect on main

The dep-sync skip trusts `ctx.noCommitsExpected` alone, and **only ever
runs on a branch that has commits** — the `rev-list --count`
short-circuit ~50 lines above returns `outcome: "empty"` at zero ahead,
so control reaches it only when the branch is AHEAD.

Nothing revalidates the flag. Both downstream empty-lane guards carve
no-commits tasks out explicitly — `merger-ai.ts:1372` (#2259
already-landed proof) and `:1994` (FN-8141 executor veto) — and both
guard the *opposite* direction: commit-expected task, empty branch. The
inverse has no check.

So a task marked no-commits whose executor committed a manifest or
lockfile change gets its dependency install **and** its frozen-lockfile
validation skipped, and the change lands unvalidated.

## The fix

The flag says *look*; the branch diff decides. A `main...branch` diff
touching `package.json` or any `LOCKFILE_CANDIDATES` entry falls through
to the normal sync and emits an audit row with `skipOverridden: true`.
An unreadable diff **also** syncs — matching the hard-fail contract
documented directly above that block, rather than treating absence of
evidence as evidence of safety.

`LOCKFILE_CANDIDATES` is exported instead of duplicated, so the skip and
the installer cannot drift on what counts as a dependency change.

**Mutation-verified:** reverting to trust-the-flag fails exactly the new
case and nothing else. The existing *"lands successfully with
noCommitsExpected: true and actual changes"* case is untouched and still
passes — `feature.txt` is not a dependency file, so an ordinary source
change on a no-commits task still skips. The new case differs only in
*which* file the branch touches.

## Also carried over from the #2501 review

**coderabbit's env nit** — `process.env.X = undefined` stores the string
`"undefined"`, leaving a previously-absent var truthy and leaking into
later tests. `restoreEnv` applied at both sites.

**Both entry paths** — deferred with reasons:
`runAiMerge`/`landWorkspaceTask` sit behind real worktrees, sessions and
a merge agent, and the cheap version is a mirrored-implementation test
that cannot fail on a revert (this repo has deleted two of those). The
fix above also means propagation is no longer the only thing between a
stale flag and an unvalidated lockfile.

## A correction to my own work

My first version of the regression committed the lockfile while the
fixture had left the tree on `main`, so the `main...branch` diff could
not see it and the case **passed for the wrong reason**. Corrected, with
the reason recorded in the test.

## Verification

- `merger-ai-no-commits-deps-skip` — **5/5**, mutation-verified
- `merge-dependency-sync-lockfile-heal` — **10/10**
- engine typecheck — clean
- `pnpm lint` — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants