Skip to content

fix(runner): validate outer hop node_modules against declared deps#1390

Merged
Chase J (chajac) merged 14 commits into
mainfrom
chajac/outer-hop-validate
Jul 10, 2026
Merged

fix(runner): validate outer hop node_modules against declared deps#1390
Chase J (chajac) merged 14 commits into
mainfrom
chajac/outer-hop-validate

Conversation

@chajac

@chajac Chase J (chajac) commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Note

PR body AI drafted & edited as needed

Overview of Changes

qawolf flows run could fail with ERR_MODULE_NOT_FOUND for a dependency the flow project correctly declares. Root cause: the outer hop discovery in populateOuterHop walked up from the flow project and symlinked the first ancestor node_modules it found, with no check that it actually contains the project's deps, and finding one suppressed the npm install fallback that would have worked.

How it works

  • Validate then walk (outerHop.ts): a candidate node_modules is symlinked only if every installable declared dep resolves in it (pinned executor packages are excluded, the inner hop owns those). Failing candidates are recorded and the walk continues; if none satisfies, the existing fallback installs the project's declared deps into the run dir (executor packages stripped, so workspace:* pins don't break the install).
  • Observability: populateOuterHop returns a result union (symlink | install | none) threaded through prepareRunDir > flows run, which renders Installing N project dependencies… before the (potentially slow) fallback install and debug logs each rejected candidate with the deps it was missing.
  • Actionable failure: residual module resolution failures get a hint naming the unresolved package and pointing at the project's package.json.
    Path like specifiers (broken ./relative imports) are excluded so the hint never suggests "installing" a file path.

Testing

  • New unit & integration tests cover nested bundle, hoisted monorepo, split hoisting, sibling repo, and fallback install topologies.
  • A/B verified on real builds: the prefix CLI reproduces the reported error on all three channels (node, bun, compiled binary). The fixed CLI passes, including against a real pulled bundle with 15 deps.
Topology Pre-fix Fixed
Flow project nested in another repo Cannot find package 'date-fns' (3/3 channels) ✅ pass — Installing 1 project dependency… (3/3)
Monorepo, deps hoisted to workspace root ✅ pass ✅ pass (no regression)
Standalone project, own node_modules ✅ pass ✅ pass (no regression)
Real pulled bundle (15 deps, latest + workspace:* specs), nested ERR_MODULE_NOT_FOUND: Cannot find package 'date-fns' ✅ pass — Installing 15 project dependencies…

Checklist

  • Changes follow the code style of this project
  • Self-review completed
  • Tests added/updated (or not applicable)
  • No breaking changes (or described below)

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3b41bb76-5928-4d3e-bfba-01b47096d23b

📥 Commits

Reviewing files that changed from the base of the PR and between 7fe31e1 and 812ee56.

📒 Files selected for processing (5)
  • .changeset/validate-outer-hop-discovery.md
  • src/domains/runtimeEnv/outerHop.test.ts
  • src/domains/runtimeEnv/outerHop.ts
  • src/domains/runtimeEnv/prepareRunDir.outerHop.test.ts
  • src/domains/runtimeEnv/runDirFixtures.testUtils.ts

Walkthrough

This PR refactors outer-hop dependency discovery to validate an ancestor node_modules directory against declared dependencies before reusing it, falling back to installing project dependencies otherwise. populateOuterHop now returns a typed result (symlink/install/none) with rejection details and an install-start callback. prepareRunDir, buildFlowsRunDeps, buildRunReporter, and runStagedFlows thread projectDir/outerHop results through the run pipeline, with UI status and debug logging added. A new extractMissingPackage helper and runnerMessages entries power a module-not-found resolution hint in the console reporter. Tests and fixtures were updated/added accordingly.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • qawolf/cli#1381: Builds on the same outer-hop/prepareRunDir/runStagedFlows runtime dependency isolation work in the same modules.
  • qawolf/cli#1386: Also modifies src/domains/runtimeEnv/prepareRunDir.ts to add projectDir-dependent behavior in the staging pipeline.

Suggested reviewers: smonn

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Uses Conventional Commits correctly and specifically describes the outer-hop dependency validation fix.
Description check ✅ Passed Covers the required overview, testing, and checklist sections; the template’s exact command block is summarized instead of copied.
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
  • Commit unit tests in branch chajac/outer-hop-validate

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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 `@src/domains/runtimeEnv/outerHop.test.ts`:
- Around line 50-51: The module-scoped installMock in outerHop.test.ts can
retain call history and implementation across it blocks because mock.restore()
does not clear a standalone mock() state. Update the test cleanup in the
outerHop test suite to call installMock.mockClear() after each test, or use
installMock.mockReset() if you also need to remove the mocked implementation,
alongside the existing afterEach cleanup.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3479e6f2-989a-40d0-be9c-8094dd5f6037

📥 Commits

Reviewing files that changed from the base of the PR and between 160004e and f69f2de.

📒 Files selected for processing (17)
  • .changeset/validate-outer-hop-discovery.md
  • src/commands/flows/buildFlowsRunDeps.ts
  • src/commands/flows/buildRunReporter.ts
  • src/commands/flows/hybridRun.test.ts
  • src/commands/flows/runDefaults.handle.test.ts
  • src/commands/flows/runDefaults.reporterWiring.test.ts
  • src/commands/flows/runStagedFlows.test.ts
  • src/commands/flows/runStagedFlows.ts
  • src/core/errors.test.ts
  • src/core/errors.ts
  • src/core/messages/runner.ts
  • src/domains/runtimeEnv/outerHop.test.ts
  • src/domains/runtimeEnv/outerHop.ts
  • src/domains/runtimeEnv/prepareRunDir.test.ts
  • src/domains/runtimeEnv/prepareRunDir.ts
  • src/shell/reporter/createConsoleReporter.test.ts
  • src/shell/reporter/createConsoleReporter.ts

Comment thread src/domains/runtimeEnv/outerHop.test.ts Outdated
@chajac Chase J (chajac) marked this pull request as ready for review July 9, 2026 16:38

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.

Mostly suggestions, nothing blocking. Good to merge after addressing them.

Comment thread .changeset/validate-outer-hop-discovery.md Outdated
Comment thread src/domains/runtimeEnv/outerHop.test.ts Outdated
Comment thread src/domains/runtimeEnv/outerHop.ts
Comment thread src/domains/runtimeEnv/prepareRunDir.test.ts Outdated
@chajac Chase J (chajac) merged commit 11016d9 into main Jul 10, 2026
6 checks passed
@chajac Chase J (chajac) deleted the chajac/outer-hop-validate branch July 10, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants