Skip to content

Make empty web test discovery version-stable - #8946

Closed
lawrencecchen wants to merge 11 commits into
mainfrom
fix-web-empty-discovery-guard
Closed

lawrencecchen wants to merge 11 commits into
mainfrom
fix-web-empty-discovery-guard

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • carries Isolate web test module globals #8891 exact head 990bf083e61d4704625d05502728d6f91343a52d
  • rejects empty default discovery before invoking Bun
  • preserves recursive discovery and explicit-argument forwarding
  • asserts the shared runner emits its own version-stable diagnostic

Why

Bun 1.2.14 exits successfully when default discovery finds no tests, while Bun 1.3.x exits nonzero. The shared runner must not delegate this repository invariant to version-dependent CLI behavior.

Review finding: #8941 (comment)

Verification

  • test-only commit 98e18193a7 fails the focused empty-discovery assertion
  • bun test tests/web-test-runner-isolation.test.ts -t "default discovery" (2 passed)
  • bash -n web/scripts/run-tests.sh
  • git diff --check

Summary by CodeRabbit

  • Bug Fixes

    • Improved Codex hook reliability when hooks are disabled or encounter errors by safely handling input and preventing stalled commands.
    • Preserved user-defined Codex hooks while correctly recognizing and updating cmux-managed hooks.
    • Improved compatibility with legacy Codex hook configurations and saved layouts.
    • Added stable, content-based hook script naming to prevent older installations from overwriting newer scripts.
  • Tests

    • Expanded regression coverage for Codex hook installation, migration, sanitization, and web test execution.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Codex hook handling now uses shared schemas and content-addressed script names, stricter launch sanitization, stdin-draining no-op commands, and expanded regression coverage. Web tests now run through a shared isolated runner with recursive discovery and CI guards.

Changes

Codex hook lifecycle

Layer / File(s) Summary
Codex hook contracts and script identities
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/*CodexHook*, Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookScriptNameTests.swift
Adds public event/schema models and content-addressed CodexHookScriptName handling, including legacy filename parsing and validation tests.
Hook generation and ownership handling
CLI/CMUXCLI+AgentHookDefinitions.swift, CLI/CMUXCLI+CodexFireAndForgetHooks.swift, cmuxTests/CLICodexHookTimeoutRegression*
Generates hooks from the current schema, uses immutable script names, drains stdin on disabled/error paths, recognizes owned Codex scripts, and adds installation/path regression tests.
Schema-based launch sanitization
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift, Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift, cmuxTests/CmuxConfigActionSaverTests.swift
Matches recognized injected hook schemas, validates script paths and configuration values, and updates sanitizer and command-line tests.

Shared web test runner

Layer / File(s) Summary
Runner implementation and validation
web/scripts/run-tests.sh, web/package.json, .github/workflows/ci.yml, web/tests/web-test-runner-isolation.test.ts, tests/test_ci_self_hosted_guard.sh
Routes package and CI tests through the shared isolated runner, validates recursive discovery and empty-test failures, and adds wiring and behavior guards.

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

Sequence Diagram(s)

sequenceDiagram
  participant CMUXCLI
  participant CodexHookScriptName
  participant Codex
  participant AgentLaunchSanitizer
  CMUXCLI->>CodexHookScriptName: derive content-addressed filename
  CodexHookScriptName-->>CMUXCLI: return canonical script name
  CMUXCLI->>Codex: emit schema-based hook arguments
  Codex->>AgentLaunchSanitizer: provide launch arguments
  AgentLaunchSanitizer-->>Codex: remove recognized cmux hook block
Loading

Possibly related PRs

Suggested reviewers: austinywang, azooz2003-bit


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Cmux No Ambient Global State ❌ Error AgentLaunchSanitizerCodexLaunch.swift exposes module-level free-function API at file scope (preservedCodexLaunchArguments, cmuxWrapperInjectedAgentNameFromArgumentPrefix) instead of an owning t... Move the Codex launch logic onto an owning type (or make helpers private/fileprivate) and keep file-scope functions out of the module API surface.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description covers summary, rationale, and verification, but it omits required template sections like Demo Video, Review Trigger, and the Checklist. Add the missing template sections: Testing, Demo Video, Review Trigger, and Checklist, or mark non-applicable items clearly.
✅ Passed checks (22 passed)
Check name Status Explanation
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.
Cmux Swift Actor Isolation ✅ Passed Only new Swift production types are immutable Sendable structs and pure helpers; no new MainActor, protocol, or shared-mutable-isolation issues were introduced.
Cmux Swift Blocking Runtime ✅ Passed Changed production Swift files only add parsing/value types and shell-string assembly; no new Swift semaphores, waits, sleeps, locks, or sync calls appear in the diff.
Cmux Browser Automation Off-Main ✅ Passed HEAD cb19b85 only changes web/scripts/run-tests.sh; no browser.* socket routing or WebKit/main-actor code was touched, so the policy doesn’t apply.
Cmux Expensive Synchronous Load ✅ Passed No touched production Swift file adds/moves agent-history loads or large JSON parsing onto main/interactive paths; changes are hook/script generation and sanitization only.
Cmux Cache Substitution Correctness ✅ Passed Only web/scripts/run-tests.sh changed here; no Swift/TS/JS persistence, history, undo, or snapshot cache substitution is present.
Cmux No Hacky Sleeps ✅ Passed The only changed runtime script, web/scripts/run-tests.sh, adds no sleeps, timers, polling, or wall-clock waits; it just preflights test discovery and runs bun test.
Cmux Algorithmic Complexity ✅ Passed web/scripts/run-tests.sh does one linear filesystem pass to detect test files; no nested rescans or repeated sort/filter hot-path work.
Cmux Swift Concurrency ✅ Passed No cmux-owned Swift files are in the PR diff; only web/shell files changed, so none of the banned async patterns were introduced.
Cmux Swift @Concurrent ✅ Passed The Swift diff adds no @concurrent/nonisolated async changes; modified functions are synchronous and the only test-support edit is a sync env var addition.
Cmux Swift Package Boundaries ✅ Passed Reusable Codex hook schema/filename/sanitization logic was moved into CMUXAgentLaunch; the CLI changes are integration glue around that package.
Cmux Swiftpm Lockfiles ✅ Passed HEAD only changes web/scripts/run-tests.sh; there are no Package.resolved, .gitignore, Xcode project, or dependency diffs to violate the rule.
Cmux Swift Logging ✅ Passed No added or changed print/debugPrint/dump/NSLog/Logger usage or file-scoped MainActor Logger constants appear in the production Swift diff.
Cmux User-Facing Error Privacy ✅ Passed The only added production-facing diagnostic is the generic 'cmux web test runner found no test files'; no vendor, env, or secret details are exposed.
Cmux Full Internationalization ✅ Passed HEAD only changes the web test runner shell script; no Swift/UI copy or locale assets were added, and the new stderr line is an operational runner diagnostic.
Cmux Swiftui State Layout ✅ Passed PR diff only touches web CI/test-runner files; no SwiftUI, ObservableObject, @Published, GeometryReader, or render-time state changes are present.
Cmux Architecture Rethink ✅ Passed HEAD only changes web/scripts/run-tests.sh; there are no Swift architecture changes to violate the rule.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The Swift edits are hook/sanitizer/test code only; no user-visible NSWindow/NSPanel/WindowGroup changes or cmuxAuxiliaryWindowIdentifiers edits.
Cmux Source Artifacts ✅ Passed Changed paths are intentional source/config/test files only; no logs, caches, temp dirs, build output, or other artifact-like entries appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No Swift files under any production Sources/ path changed versus origin/main, so the no-test/debug-seam rule is not applicable.
Title check ✅ Passed The title clearly matches the main change: making empty web test discovery version-stable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-web-empty-discovery-guard

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.

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 26, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lawrencecchen
lawrencecchen changed the base branch from fix/founders-welcome-test-isolation to main July 26, 2026 02:06
@greptile-apps

greptile-apps Bot commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR centralizes Codex hook schema and script identity handling while introducing a shared, isolated web-test runner.

  • Adds content-addressed Codex hook scripts and stricter replay sanitization for current and legacy injection schemas.
  • Makes disabled and fallback hook paths drain standard input before acknowledging the event.
  • Routes web CI and bun run test through a recursive discovery guard with per-file isolation.
  • Expands Swift, shell, and Bun regression coverage around hook handling, CI guards, and test-runner behavior.

Confidence Score: 4/5

The PR is safe to merge with non-blocking issues in web-test discovery parity and generated-hook ownership recognition.

The web guard does not exactly mirror Bun's supported test names, and Codex hook cleanup treats a syntactically valid generated-script filename as ownership proof without validating its content.

Files Needing Attention: web/scripts/run-tests.sh; Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookScriptName.swift

Important Files Changed

Filename Overview
web/scripts/run-tests.sh Adds the stable empty-discovery guard and isolated Bun invocation, but its filename approximation differs from Bun's discovery rules.
web/tests/web-test-runner-isolation.test.ts Adds regression coverage for isolation, recursive discovery, and empty default discovery.
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift Tightens injected Codex hook stripping around complete, ordered schemas and generated-script paths, with ownership inherited from filename parsing.
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionSchema.swift Establishes a shared source of truth for current and explicitly supported historical hook blocks.
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookScriptName.swift Introduces content-addressed and legacy script-name parsing, but filename parsing alone is also used as ownership proof.
CLI/CMUXCLI+CodexFireAndForgetHooks.swift Uses shared hook schemas and immutable content-addressed scripts while draining input on no-op paths.
.github/workflows/ci.yml Routes web CI through the shared package test command.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["bun run test"] --> B["scripts/run-tests.sh"]
  B --> C{"Explicit arguments?"}
  C -->|Yes| D["bun test --isolate arguments"]
  C -->|No| E["Pre-scan default test files"]
  E -->|Found| F["bun test --isolate"]
  E -->|None| G["Stable cmux diagnostic and exit 1"]
Loading

Comments Outside Diff (1)

  1. Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookScriptName.swift, line 43-60 (link)

    P2 Validate content-addressed ownership

    This parser accepts the hexadecimal filename component without checking it against the script contents, while hook cleanup and replay sanitization use a successful parse as proof that the command is cmux-owned. A custom script under ~/.cmux/hooks with a matching filename is therefore removed from hook configuration or stripped from replay arguments even when cmux did not generate it.

    Knowledge Base Used: CLI tool (cmux)

Reviews (1): Last reviewed commit: "Fix empty web test discovery across Bun ..." | Re-trigger Greptile

Comment thread web/scripts/run-tests.sh
*) continue ;;
esac
case "${file##*/}" in
*".test"* | *"_test_"* | *".spec"* | *"_spec_"*) return 0 ;;

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.

P2 Align pre-discovery with Bun

The basename substrings do not match Bun's default filename rules: *_test_* misses a valid name such as example_test.ts, while *.test* accepts a helper such as example.test-helper.ts. This can reject a valid default suite or delegate an actually empty suite back to Bun's version-dependent behavior.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Superseded by the concurrently updated #8891 at 30965cefdaf3a32e3ca945e7b31719cffc9903f2, which implements the same Bun-version compatibility repair with broader discovery, configuration, cleanup, and agent-mode coverage. PR 8941 will consume that reviewed exact head.

@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: cb19b85071

ℹ️ 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 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 address that feedback".


if ! PATH="$fixture_dir/bin:/usr/bin:/bin" \
CMUX_WEB_TEST_RUNNER_ARGS_LOG="$args_log" \
/bin/bash "$fixture_runner"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add a discovered test file to the guard fixture

This invokes the new runner with no arguments, but the fixture contains only web/scripts/run-tests.sh and the fake bun executable. Consequently, default_test_file_exists exits before the fake Bun is called; running bash tests/test_ci_self_hosted_guard.sh produces cmux web test runner found no test files followed by FAIL: shared web test runner default discovery should execute. Since .github/workflows/ci.yml runs this script in workflow-guard-tests, that required job now fails on every run; create a correctly named fixture test before invoking the runner.

Useful? React with 👍 / 👎.

@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: 6

🤖 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 `@CLI/CMUXCLI`+AgentHookDefinitions.swift:
- Around line 437-444: Update isCmuxOwnedCodexHookScriptCommand to obtain the
Codex hooks directory path without calling codexHookScriptsDirectory(), since
that helper creates the directory as a side effect. Reuse or introduce a
non-creating path resolver for the ownership check, preserving the existing
filename and standardized-path comparisons.

In `@cmuxTests/CmuxConfigActionSaverTests.swift`:
- Around line 497-506: Strengthen the assertions around the generated Codex hook
arguments so they independently verify that hook stripping still removes the
stop-event hook, rather than relying only on the hardcoded
“cmux-codex-hook-stop.sh” name. Use schema-derived data or another
schema-independent invariant, while preserving the existing assertions for the
current stop subcommand.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift`:
- Around line 255-283: Update cmuxCodexHookScriptFilename to reject commands
containing token separators or arguments before returning a hook filename;
validate that normalizedCommand is exactly one complete, recognizable hook
script path under .cmux/hooks, including the expected hook-script
naming/extension, while preserving valid paths with interior component spaces.

In
`@Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift`:
- Around line 578-584: The schema-derived fixture in
Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift#L578-L584
must become an independent literal list for codexWrapperHookEvents, matching the
pinned legacySavedLayoutHookEvents and legacyAliasHookEvents fixtures so schema
changes require explicit recognized entries. In
cmuxTests/CmuxConfigActionSaverTests.swift#L497-L506, retain the schema-derived
fixture but update the relevant assertions to check for “cmux-codex-hook” and
“--enable hooks” instead of the hardcoded stop filename, preventing vacuous
passes.

In `@tests/test_ci_self_hosted_guard.sh`:
- Around line 869-900: Update check_web_test_runner_behavior to create a minimal
valid test fixture under fixture_dir before invoking fixture_runner, ensuring
the runner passes its discovery guard and delegates to the Bun stub. Keep the
existing args_log assertion and cleanup behavior unchanged.

In `@web/scripts/run-tests.sh`:
- Around line 7-20: Update default_test_file_exists to mirror Bun’s discovery
rules exactly: restrict matching to Bun-supported test filename patterns, avoid
partial matches such as foo.test-helper.ts, and prune every ignored node_modules
and hidden directory during find traversal, including nested paths. Preserve the
function’s existing return behavior by returning success only when a
Bun-discoverable test file is found.
🪄 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: 18f14d07-ea5b-4a12-b350-b94228db8b6a

📥 Commits

Reviewing files that changed from the base of the PR and between 5e26dd3 and cb19b85.

📒 Files selected for processing (16)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • CLI/CMUXCLI+CodexFireAndForgetHooks.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionEvent.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionSchema.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookScriptName.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookScriptNameTests.swift
  • cmuxTests/CLICodexHookTimeoutRegressionTestSupport.swift
  • cmuxTests/CLICodexHookTimeoutRegressionTests.swift
  • cmuxTests/CmuxConfigActionSaverTests.swift
  • tests/test_ci_self_hosted_guard.sh
  • web/package.json
  • web/scripts/run-tests.sh
  • web/tests/web-test-runner-isolation.test.ts

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 6

🤖 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 `@CLI/CMUXCLI`+AgentHookDefinitions.swift:
- Around line 437-444: Update isCmuxOwnedCodexHookScriptCommand to obtain the
Codex hooks directory path without calling codexHookScriptsDirectory(), since
that helper creates the directory as a side effect. Reuse or introduce a
non-creating path resolver for the ownership check, preserving the existing
filename and standardized-path comparisons.

In `@cmuxTests/CmuxConfigActionSaverTests.swift`:
- Around line 497-506: Strengthen the assertions around the generated Codex hook
arguments so they independently verify that hook stripping still removes the
stop-event hook, rather than relying only on the hardcoded
“cmux-codex-hook-stop.sh” name. Use schema-derived data or another
schema-independent invariant, while preserving the existing assertions for the
current stop subcommand.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift`:
- Around line 255-283: Update cmuxCodexHookScriptFilename to reject commands
containing token separators or arguments before returning a hook filename;
validate that normalizedCommand is exactly one complete, recognizable hook
script path under .cmux/hooks, including the expected hook-script
naming/extension, while preserving valid paths with interior component spaces.

In
`@Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift`:
- Around line 578-584: The schema-derived fixture in
Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift#L578-L584
must become an independent literal list for codexWrapperHookEvents, matching the
pinned legacySavedLayoutHookEvents and legacyAliasHookEvents fixtures so schema
changes require explicit recognized entries. In
cmuxTests/CmuxConfigActionSaverTests.swift#L497-L506, retain the schema-derived
fixture but update the relevant assertions to check for “cmux-codex-hook” and
“--enable hooks” instead of the hardcoded stop filename, preventing vacuous
passes.

In `@tests/test_ci_self_hosted_guard.sh`:
- Around line 869-900: Update check_web_test_runner_behavior to create a minimal
valid test fixture under fixture_dir before invoking fixture_runner, ensuring
the runner passes its discovery guard and delegates to the Bun stub. Keep the
existing args_log assertion and cleanup behavior unchanged.

In `@web/scripts/run-tests.sh`:
- Around line 7-20: Update default_test_file_exists to mirror Bun’s discovery
rules exactly: restrict matching to Bun-supported test filename patterns, avoid
partial matches such as foo.test-helper.ts, and prune every ignored node_modules
and hidden directory during find traversal, including nested paths. Preserve the
function’s existing return behavior by returning success only when a
Bun-discoverable test file is found.
🪄 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: 18f14d07-ea5b-4a12-b350-b94228db8b6a

📥 Commits

Reviewing files that changed from the base of the PR and between 5e26dd3 and cb19b85.

📒 Files selected for processing (16)
  • .github/workflows/ci.yml
  • CLI/CMUXCLI+AgentHookDefinitions.swift
  • CLI/CMUXCLI+CodexFireAndForgetHooks.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionEvent.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionSchema.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookScriptName.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookScriptNameTests.swift
  • cmuxTests/CLICodexHookTimeoutRegressionTestSupport.swift
  • cmuxTests/CLICodexHookTimeoutRegressionTests.swift
  • cmuxTests/CmuxConfigActionSaverTests.swift
  • tests/test_ci_self_hosted_guard.sh
  • web/package.json
  • web/scripts/run-tests.sh
  • web/tests/web-test-runner-isolation.test.ts
🛑 Comments failed to post (6)
CLI/CMUXCLI+AgentHookDefinitions.swift (1)

437-444: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Ownership predicate creates ~/.cmux/hooks as a side effect.

codexHookScriptsDirectory() calls createDirectory(withIntermediateDirectories:), so this read-only check writes to the filesystem — including during uninstall/prune scans where the directory may have just been removed. Resolve the path without creating it here.

🤖 Prompt for 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.

In `@CLI/CMUXCLI`+AgentHookDefinitions.swift around lines 437 - 444, Update
isCmuxOwnedCodexHookScriptCommand to obtain the Codex hooks directory path
without calling codexHookScriptsDirectory(), since that helper creates the
directory as a side effect. Reuse or introduce a non-creating path resolver for
the ownership check, preserving the existing filename and standardized-path
comparisons.
cmuxTests/CmuxConfigActionSaverTests.swift (1)

497-506: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assertions can go vacuous if the schema's stop subcommand is renamed.

The fixture is now schema-derived, but Lines 361 and 377 still assert against the hardcoded "cmux-codex-hook-stop.sh". If CodexHookInjectionSchema.current renames that subcommand, the substring is never generated and both assertions pass while verifying nothing. Add a schema-independent assertion so the test keeps failing when stripping breaks.

💚 Suggested strengthening
-        `#expect`(nativeCommand?.contains("cmux-codex-hook-stop.sh") == false)
+        `#expect`(nativeCommand?.contains("cmux-codex-hook") == false)
+        `#expect`(nativeCommand?.contains("--enable hooks") == false)
🤖 Prompt for 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.

In `@cmuxTests/CmuxConfigActionSaverTests.swift` around lines 497 - 506,
Strengthen the assertions around the generated Codex hook arguments so they
independently verify that hook stripping still removes the stop-event hook,
rather than relying only on the hardcoded “cmux-codex-hook-stop.sh” name. Use
schema-derived data or another schema-independent invariant, while preserving
the existing assertions for the current stop subcommand.
Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift (1)

255-283: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Show the full helper and any other callers that trust its result.
ast-grep outline Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift --items all
rg -nP --type=swift -C3 'cmuxCodexHookScriptFilename' Packages

Repository: manaflow-ai/cmux

Length of output: 3329


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift | sed -n '1,290p'
echo '--- behavioral probe ---'
python3 - <<'PY'
# Minimal semantic probe for the path/component checks in AgentLaunchSanitizerCodexLaunch.swift.
import posixpath

codex_hook_shell_metacharacters = set(";&|<>`$'\"\\ \t\n")
def cmux_codex_hook_script_filename(normalized_command: str):
    if not normalized_command.startswith("/"):
        return None
    if any(ord(c) <= 32 for c in normalized_command):  # control chars; Space 32 is handled below
        return None
    if any(c in codex_hook_shell_metacharacters for c in normalized_command):
        return None
    # Swift URL(fileURL...).standardizedFileURL does not shell-expand; use posixpath normalization here.
    # URL normalization for embedded URL arguments/token separators would not produce these path components.
    components = posixpath.normpath(normalized_command).split("/")
    # Strip empty first due leading slash; URL pathComponents has root as empty, last pathComponent as final component.
    # The Swift guard compares URL path == normalizedCommand but split pathComponents can include root component, so align.
    normalized_normalized = normalized_command.rstrip("/") or "/"
    split_path = normalized_normalized.split("/")
    # The check below is analogous to URL.pathComponents.allSatisfy trim: no boundary whitespace in path components,
    # preserving interior whitespace.
    for p in split_path:
        if p != p.strip():
            return None
    # Suffix .cmux hooks is on directoryComponents, which excludes lastPathComponent
    directory_components = posixpath.normpath(normalized_command).rsplit("/",1)[0].split("/") if "/" in normalized_normalized.lstrip("/") else []
    # Leading root is first empty component because normpath('/') is '/'
    # Actually: for '/usr/bin/foo', components include '', 'usr', 'bin', 'foo'.
    components = posixpath.normpath(normalized_command).split("/")
    directory_components = components[:-1]
    if len(directory_components) < 2:
        return None
    if directory_components[-2:] != ['.cmux', 'hooks']:
        return None
    return components[-1]

for command in [
    "/usr/bin/env sh relative/.cmux/hooks/cmux-codex-hook-<id>-stop.sh",
    "/usr/local/bin/cmux-agent .cmux/hooks/cmux-codex-hook-<id>-stop.sh",
    "/home/user/.cmux/hooks/cmux-codex-hook-<id>-stop.sh",
]:
    print(command, cmux_codex_hook_script_filename(command))
PY

Repository: manaflow-ai/cmux

Length of output: 13616


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo '--- tests mentioning codex hooks / injection / sanitization ---'
rg -nP --type-test --glob '*Codex*|*Launch*|*Sanitizer*' -C3 'hooks|codex|cmuxCodex|CodexHook|replay|preserve|stripCmux' . || true
echo '--- test files containing hook command samples ---'
rg -n --type-test -C3 "cmux-codex-hook|\\.cmux/hooks|hooks codex|\\[\\{hooks" . || true
echo '--- all references to hook command filename/schema ---'
rg -n --type=swift -C3 'CodexHookScriptName|codexWrapperInjectedHookSubcommandAliases|cmuxCodexHookScriptFilename|recognized' Packages || true

Repository: manaflow-ai/cmux

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
# Read-only semantic probe matching the exact Swift guard:
# 1. first metacharacters guard includes space/tabs/control;
# 2. URL(fileURLWithPath: path, isDirectory: false).standardizedFileURL preserves path components with spaces;
# 3. URL path equality preserves spacing;
# 4. pathComponents trim only boundary whitespace.
# Use the set of shell metacharacters from codexHookShellMetacharacters, excluding the metacharacters it already rejects,
# then test interior space in a path component as the review describes.
import urllib.parse, os

codex_hook_shell_metacharacters = set(";&|<>`$'\"\\\\ \t\n")
def cmux_codex_hook_script_filename(normalized_command: str):
    if not normalized_command.startswith("/"):
        return None
    if any(ord(c) <= 32 for c in normalized_command):
        return None
    if any(c in codex_hook_shell_metacharacters for c in normalized_command):
        return None
    url_path = urllib.parse.unquote(urllib.parse.urlparse(normalized_command).path)
    if url_path != normalized_command:
        return None
    parts = urllib.parse.urlparse(normalized_command).path.split("/")
    parts2 = urllib.parse.urlparse(normalized_command).path.replace("//", "/").lstrip("/").split("/")
    normalized_normalized = normalized_command.rstrip("/") or "/"
    parts = normalized_normalized.split("/")
    if any(p != p.strip() for p in parts):
        return None
    directory_components = parts[:-1]
    if len(directory_components) < 2:
        return None
    if directory_components[-2:] != [".cmux", "hooks"]:
        return None
    return parts[-1]

cases = [
    "/usr/local/bin/cmux-agent .cmux/hooks/cmux-codex-hook-a-stop.sh",
    "/usr/local/bin/cmux-agent  .cmux/hooks/cmux-codex-hook-a-stop.sh",
    "/usr/local/bin/cmux-agent  .cmux/hooks/cmux-codex-hook-a-stop.sh",
    "/home/user/My App/.cmux/hooks/cmux-codex-hook-a-stop.sh",
    "/.cmux/hooks/cmux-codex-hook-a-stop.sh",
]
for command in cases:
    print(command, cmux_codex_hook_script_filename(command))
PY

echo '--- relevant CodexHookInjectionSchema.swift ---'
cat -n Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/CodexHookInjectionSchema.swift | sed -n '1,120p'

Repository: manaflow-ai/cmux

Length of output: 3311


Reject multi-token Codex hook commands before stripping hook arguments.

cmuxCodexHookScriptFilename only trims boundary whitespace per path component, so commands like /usr/local/bin/cmux-agent .cmux/hooks/cmux-codex-hook-a-stop.sh satisfy the path check and can remove the hook as cmux-injected config. Require the command to be the complete single-file path of a recognizable hook script, or explicitly reject non-trailing-command tokens before stripping.

🤖 Prompt for 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.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchSanitizerCodexLaunch.swift`
around lines 255 - 283, Update cmuxCodexHookScriptFilename to reject commands
containing token separators or arguments before returning a hook filename;
validate that normalizedCommand is exactly one complete, recognizable hook
script path under .cmux/hooks, including the expected hook-script
naming/extension, while preserving valid paths with interior component spaces.
Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift (1)

578-584: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Schema-derived hook fixtures remove the independent oracle from the stripping tests. Both suites replaced pinned literal argv with fixtures generated from CodexHookInjectionSchema.current, so a change to the production schema updates the expectation and the input together — the tests can no longer detect a shape change that breaks stripping of argv captured by an older build.

  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift#L578-L584: pin the current event list literally, as legacySavedLayoutHookEvents and legacyAliasHookEvents already are, so a schema change forces the previous shape to be added to recognized.
  • cmuxTests/CmuxConfigActionSaverTests.swift#L497-L506: keep the schema-derived fixture but make the Line 361/377 assertions schema-independent (assert on "cmux-codex-hook" / "--enable hooks" rather than the hardcoded stop filename) so they cannot pass vacuously.
📍 Affects 2 files
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift#L578-L584 (this comment)
  • cmuxTests/CmuxConfigActionSaverTests.swift#L497-L506
🤖 Prompt for 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.

In
`@Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift`
around lines 578 - 584, The schema-derived fixture in
Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/CodexHookInjectionStrippingTests.swift#L578-L584
must become an independent literal list for codexWrapperHookEvents, matching the
pinned legacySavedLayoutHookEvents and legacyAliasHookEvents fixtures so schema
changes require explicit recognized entries. In
cmuxTests/CmuxConfigActionSaverTests.swift#L497-L506, retain the schema-derived
fixture but update the relevant assertions to check for “cmux-codex-hook” and
“--enable hooks” instead of the hardcoded stop filename, preventing vacuous
passes.
tests/test_ci_self_hosted_guard.sh (1)

869-900: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Create a fixture test before asserting Bun delegation.

The fixture has no test files, so the runner exits at its new empty-discovery guard and never invokes the Bun stub. This check now fails unconditionally.

Proposed fix
-  mkdir -p "$fixture_dir/web/scripts" "$fixture_dir/bin"
+  mkdir -p "$fixture_dir/web/scripts" "$fixture_dir/web/tests" "$fixture_dir/bin"
   cp "$ROOT_DIR/web/scripts/run-tests.sh" "$fixture_runner"
+  : > "$fixture_dir/web/tests/default.test.ts"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

check_web_test_runner_behavior() {
  local fixture_dir fixture_runner args_log expected_args
  fixture_dir="$(mktemp -d)"
  fixture_runner="$fixture_dir/web/scripts/run-tests.sh"
  args_log="$fixture_dir/bun-args.log"
  mkdir -p "$fixture_dir/web/scripts" "$fixture_dir/web/tests" "$fixture_dir/bin"
  cp "$ROOT_DIR/web/scripts/run-tests.sh" "$fixture_runner"
  : > "$fixture_dir/web/tests/default.test.ts"

  cat > "$fixture_dir/bin/bun" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$@" > "$CMUX_WEB_TEST_RUNNER_ARGS_LOG"
EOF
  chmod +x "$fixture_dir/bin/bun"

  if ! PATH="$fixture_dir/bin:/usr/bin:/bin" \
    CMUX_WEB_TEST_RUNNER_ARGS_LOG="$args_log" \
    /bin/bash "$fixture_runner"; then
    rm -rf "$fixture_dir"
    echo "FAIL: shared web test runner default discovery should execute"
    exit 1
  fi

  expected_args=$'test\n--isolate'
  if [[ "$(cat "$args_log")" != "$expected_args" ]]; then
    echo "FAIL: shared web test runner must preserve Bun's default discovery with isolation"
    cat "$args_log"
    rm -rf "$fixture_dir"
    exit 1
  fi

  rm -rf "$fixture_dir"
}
🤖 Prompt for 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.

In `@tests/test_ci_self_hosted_guard.sh` around lines 869 - 900, Update
check_web_test_runner_behavior to create a minimal valid test fixture under
fixture_dir before invoking fixture_runner, ensuring the runner passes its
discovery guard and delegates to the Bun stub. Keep the existing args_log
assertion and cleanup behavior unchanged.
web/scripts/run-tests.sh (1)

7-20: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For the Bun version pinned by this repository, what exact default bun test filename patterns and directory exclusions apply during recursive discovery?

💡 Result:

By default, bun test recursively searches the project directory for files that match any of the following filename patterns [1][2]: *.test.{js|jsx|ts|tsx|mjs|cjs|mts|cts} *_test.{js|jsx|ts|tsx|mjs|cjs|mts|cts} *.spec.{js|jsx|ts|tsx|mjs|cjs|mts|cts} *_spec.{js|jsx|ts|tsx|mjs|cjs|mts|cts} During recursive discovery, bun test automatically ignores the following by default [1][2]: node_modules directories Hidden directories (those starting with a period .) Files that do not have JavaScript-like extensions (as determined by available loaders) You can further customize which files are excluded from discovery by using the pathIgnorePatterns option in your bunfig.toml file, which supports glob patterns [3][4]. Additionally, bun test filters passed as positional arguments are used to restrict the set of discovered tests; these filters are treated as substring matches rather than glob patterns [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- repo files ---\n'
git ls-files | rg '(^|/)web/scripts/run-tests\.sh$|(^|/)bun\.lockb$|(^|/)package\.json$|(^|/)bunfig\.toml$' || true

printf '\n--- run-tests.sh ---\n'
if [ -f web/scripts/run-tests.sh ]; then
  cat -n web/scripts/run-tests.sh
fi

printf '\n--- bun version files ---\n'
for f in package.json bun.lockb bunfig.toml; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,160p' "$f"
  fi
done

Repository: manaflow-ai/cmux

Length of output: 245


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Deterministic behavioral probe: compare the shell preflight pattern to Bun’s documented
# default filename patterns and exclusions over a representative set of paths.
python3 - <<'PY'
from pathlib import PurePosixPath

extensions = {'.js','.jsx','.ts','.tsx','.mjs','.cjs','.mts','.cts'}
allowed_extension = lambda p: PurePosixPath(p).suffix in extensions

def shell_preflight(path):
    base = PurePosixPath(path).name
    if not allowed_extension(path):
        return False
    segments = path.strip('/').split('/')
    # current implementation prunes visible ./node_modules and .next only after
    # traversing, so a deep hidden or nested node_modules is still visited and tested.
    for seg in segments:
        if seg in {'node_modules','.next'}:
            # it still would have reached hidden/nested node_modules files before pruning,
            # so include them for this preflight probe.
            pass
        if seg.startswith('.') and seg not in {'.','..' }:
            pass
    # filename matcher from the snippet:
    patterns = ('*.test', '_test_', '*.spec', '_spec_')
    return any(base.endswith(nfx) or base.startswith(nfx) for (p,nfx) in patterns)

def bun_discovery(path):
    base = PurePosixPath(path).name
    if not allowed_extension(path):
        return False
    for seg in PurePosixPath(path).parts:
        if seg.startswith('.'):
            return False
        if seg == 'node_modules':
            return False
    # exact suffix-only patterns (including all documented loader-like extensions):
    suffixes = (
        '.test.js','.test.jsx','.test.ts','.test.tsx','.test.mjs','.test.cjs','.test.mts','.test.cts',
        '.spec.js','.spec.jsx','.spec.ts','.spec.tsx','.spec.mjs','.	spec.cjs','.spec.mts','..spec.cts',
        '_test.js','_test.jsx','_test.ts','_test.tsx','_test.mjs','_test.cjs','_test.mts','_test.cts',
        '_spec.js','_spec.jsx','_spec.ts','_spec.tsx','_spec.mjs','_spec.cjs','_spec.mts','_spec.cts'
    )
    return base.endswith(suffixes)

samples = [
    './foo.test-helper.ts',
    './__tests__/foo.test.ts',
    './.hidden/foo.test.ts',
    './foo.test.ts',
    './foo_test.ts',
    './foo.test_helper.ts',
    './hidden_module/foo.test.ts',
    './node_modules/foo.test.ts',
    './lib/node_modules/foo.test.ts',
    './foo.helper.testHelper.ts',
]

for p in samples:
    print(p, "shell=", shell_preflight(p), "bun=", bun_discovery(p))

mism = [(p, shell_preflight(p), bun_discovery(p)) for p in samples if shell_preflight(p) != bun_discovery(p)]
print("\nmismatches:", mism)
PY

Repository: manaflow-ai/cmux

Length of output: 365


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- repo files ---"
git ls-files | grep -E '(^|/)web/scripts/run-tests\.sh$|(^|/)bun\.lockb$|(^|/)package\.json$|(^|/)bunfig\.toml$' || true

echo
echo "--- run-tests.sh ---"
if [ -f web/scripts/run-tests.sh ]; then
  nl -ba web/scripts/run-tests.sh
fi

echo
echo "--- bun version files ---"
for f in package.json bun.lockb bunfig.toml; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,160p' "$f"
  fi
done

Repository: manaflow-ai/cmux

Length of output: 497


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Behavioral probe: compare the shell preflight matcher/exclusion to Bun's documented test discovery rules.
python3 - <<'PY'
from pathlib import PurePosixPath

extensions = {'.js','jsx'.replace('j','j'),'.ts','.tsx','.mjs','.cjs','.mts','.cts'}
extensions = {'.js','.jsx','.ts','.tsx','.mjs','.cjs','.mts','.cts'}

def shell_path_pruned(path):
    # Original find runs from current directory and first searches for -path './node_modules' and -path '
    prunes = []
    for i, seg in enumerate(PurePosixPath(path).parts):
        # path "./node_modules" exactly prunes top-level node_modules; nested like lib/node_modules is not caught by "-path ./node_modules".
        # Hidden directory pruning is not in the original find, so paths under . are not pruned.
        pass
    return prunes

def shell_preflight(path):
    p = PurePosixPath(path)
    if p.suffix not in extensions:
        return False
    base = p.name
    # Matches anything containing .test / _test_ / .spec / _spec_ in the filename.
    return any(x in base for x in ['.test','_test_','.spec','_spec_'])

def bun_discovery(path):
    p = PurePosixPath(path)
    suffix = p.suffix
    if not (suffix.startswith('.') and suffix[1:] in {'js','jsx','ts','tsx','mjs','cjs','mts','cts'}):
        return False
    segments = PurePosixPath(path).parts
    for seg in segments:
        if seg.startswith('.') or seg == 'node_modules':
            return False
    base = p.name
    exact = [
        ['.test.js','.test.jsx','.test.ts','.test.tsx','.test.mjs','.test.cjs','.test.mts','.test.cts'],
        ['_test.js','_test.jsx','_test.ts','_test.tsx','_test.mjs','_test.cjs','_test.mts','_test.cts'],
        ['.spec.js','.spec.jsx','.spec.ts','.spec.tsx','.spec.mjs','.spec.cjs','.spec.mts','.spec.cts'],
        ['_spec.js','_spec.jsx','_spec.ts','_spec.tsx','_spec.mjs','_spec.cjs','_spec.mts','_spec.cts'],
    ]
    return any(base == suffix_for + ext for suffix_for in ['test','spec'] for ext in suffixes_for_ext)

samples = [
    './foo.test-helper.ts',
    './__tests__/foo.test.ts',
    './.hidden/foo.test.ts',
    './foo.test.ts',
    './foo_test.ts',
    './foo.test_helper.ts',
    './lib/node_modules/foo.test.ts',
    './node_modules/foo.test.ts',
    './foo.helper.testHelper.ts',
]

def shell_file_seen(path):
    # Original find scans everything unless path is exactly "./node_modules" or "./.next"; hidden files are not pruned.
    return True

for p in samples:
    print(f"{p} shell_found={shell_file_seen(p)} shell_preflight={shell_preflight(p)} bun_discovery={bun_discovery(p)}")
PY

Repository: manaflow-ai/cmux

Length of output: 362


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- files ---'
git ls-files | grep -E '(^|/)web/scripts/run-tests\.sh$|(^|/)web/bunfig\.toml$|(^|/)web/package\.json$|(^|/)package\.json$|(^|/)bun\.lockb$' || true

echo
echo '--- web/scripts/run-tests.sh ---'
sed -n '1,120p' web/scripts/run-tests.sh

echo
echo '--- web/bunfig.toml ---'
if [ -f web/bunfig.toml ]; then sed -n '1,160p' web/bunfig.toml; fi

echo
echo '--- bun config refs ---'
grep -RInE '(bun|bunfig|test|node_modules|exclude|ignore)' package.json web/package.json web/bunfig.toml 2>/dev/null || true

Repository: manaflow-ai/cmux

Length of output: 3323


🌐 Web query:

Bun test discovery hidden directories node_modules default patterns exact * .test .spec _test _spec extensions

💡 Result:

Bun's test runner automatically discovers and executes test files by recursively searching the project directory for files that match specific naming patterns [1][2]. Default Discovery Patterns: Bun identifies test files by looking for the following suffixes, supporting common JavaScript and TypeScript extensions (.js,.jsx,.ts,.tsx,.mjs,.cjs,.mts,.cts) [1][3]: - .test. - _test. - .spec. - _spec. Default Exclusions: To optimize performance and prevent execution of unintended files, Bun automatically ignores [1]: - node_modules directories - Hidden directories (any directory starting with a period .) - Files lacking recognized JavaScript-like extensions Customizing Discovery: You can further control test discovery using the following methods: - Path Ignore Patterns: You can exclude specific directories or files (such as vendor folders or fixtures) from discovery by configuring pathIgnorePatterns in your bunfig.toml file [4][5]. This is highly efficient as Bun prunes these directories during the initial scan [4]. - Root Directory: By default, Bun searches from the current working directory, but you can specify a different starting point using the root configuration in bunfig.toml [1][5]. - Filtering: While not for discovery, you can limit execution by passing positional arguments to the bun test command. Any file with a path matching the provided string filter will be executed [1][2]. Note that these are substring matches, not glob patterns [1][3].

Citations:


Mirror Bun’s default discovery rules exactly.

This preflight passes files Bun will not run and scans paths Bun ignores. It accepts partial names like foo.test-helper.ts and traverses hidden and nested node_modules directories, so it can report “tests found” when Bun would discover none.

Proposed fix
-    case "${file##*/}" in
-      *".test"* | *"_test_"* | *".spec"* | *"_spec_"*) return 0 ;;
+    case "${file##*/}" in
+      *.test.js | *.test.jsx | *.test.ts | *.test.tsx | \
+      *_test.js | *_test.jsx | *_test.ts | *_test.tsx | \
+      *.spec.js | *.spec.jsx | *.spec.ts | *.spec.tsx | \
+      *_spec.js | *_spec.jsx | *_spec.ts | *_spec.tsx) return 0 ;;
     esac
   done < <(
     find . \
-      \( -path "./node_modules" -o -path "./.next" \) -prune \
+      \( -type d \( -name node_modules -o -path '*/.*' \) -prune \) \
       -o -type f -print0
   )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

default_test_file_exists() {
  while IFS= read -r -d '' file; do
    case "$file" in
      *.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs | *.mts | *.cts) ;;
      *) continue ;;
    esac
    case "${file##*/}" in
      *.test.js | *.test.jsx | *.test.ts | *.test.tsx | \
      *_test.js | *_test.jsx | *_test.ts | *_test.tsx | \
      *.spec.js | *.spec.jsx | *.spec.ts | *.spec.tsx | \
      *_spec.js | *_spec.jsx | *_spec.ts | *_spec.tsx) return 0 ;;
    esac
  done < <(
    find . \
      \( -type d \( -name node_modules -o -path '*/.*' \) -prune \) \
      -o -type f -print0
  )
🤖 Prompt for 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.

In `@web/scripts/run-tests.sh` around lines 7 - 20, Update
default_test_file_exists to mirror Bun’s discovery rules exactly: restrict
matching to Bun-supported test filename patterns, avoid partial matches such as
foo.test-helper.ts, and prune every ignored node_modules and hidden directory
during find traversal, including nested paths. Preserve the function’s existing
return behavior by returning success only when a Bun-discoverable test file is
found.

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.

1 participant