Skip to content

fix: Windows compatibility + cross-platform e2e testing harness#24

Merged
spencermarx merged 20 commits intomainfrom
hotfix/issue-23-windows-and-cross-platform-compatibility
Apr 3, 2026
Merged

fix: Windows compatibility + cross-platform e2e testing harness#24
spencermarx merged 20 commits intomainfrom
hotfix/issue-23-windows-and-cross-platform-compatibility

Conversation

@spencermarx
Copy link
Copy Markdown
Owner

@spencermarx spencermarx commented Apr 3, 2026

Summary

  • Fixes the Windows dashboard crash reported in Dashboard fails on Windows: ESM loader rejects Windows absolute paths #23 — ESM loader rejects bare C:\ paths, .cmd shims fail without shell
  • Introduces @open-code-review/platform shared Nx workspace library with cross-platform utilities (importModule, execBinary, spawnBinary)
  • Adds e2e smoke tests for both CLI and dashboard as dedicated Nx projects
  • Establishes CI with GitHub Actions — unit tests on Ubuntu, e2e on a 3-OS matrix (Ubuntu, macOS, Windows)
  • Adds manual cross-platform testing via workflow_dispatch with optional SSH debug session

Changes

Platform fixes

  • importModule() wraps pathToFileURL + import() — fixes Received protocol 'c:' on Windows
  • execBinary() wraps execFileSync with shell: true on Windows — fixes .cmd shim detection
  • spawnBinary() wraps spawn with shell: true + windowsHide: true on Windows
  • Build step rm -rf/cp -r replaced with cross-platform fs.rmSync/fs.cpSync

Testing infrastructure

  • @nx/vitest:test executor across all packages (replaces raw nx:run-commands)
  • tsconfig.spec.json + v8 coverage config for each package
  • packages/cli-e2e/ — 7 CLI smoke tests (version, help, init, doctor, state, unknown command)
  • packages/dashboard-e2e/ — 8 dashboard smoke tests (health, auth, config, sessions, stats, commands, reviewers)
  • /api/health moved above auth middleware (accessible without token for readiness probes)

CI workflows

  • ci.yml — automated on push/PR with cross-platform e2e matrix
  • cross-platform-test.yml — manual workflow_dispatch with OS picker + optional tmate SSH debug

Test plan

  • nx run-many -t test — 362 unit tests pass (platform, cli, dashboard)
  • nx e2e cli-e2e — 7 CLI smoke tests pass
  • nx e2e dashboard-e2e — 8 dashboard smoke tests pass
  • nx build cli — build succeeds with new platform imports
  • CI workflow runs on all 3 OS after merge

Closes #23

🤖 Generated with claude-flow

spencermarx and others added 10 commits April 3, 2026 00:46
Non-buildable Nx workspace lib at packages/shared/platform with
cross-platform utilities (importModule, execBinary, spawnBinary).
Consumers inline via esbuild at build time.

Closes #23

Co-Authored-By: claude-flow <ruv@ruv.net>
Tests importModule, execBinary, and spawnBinary against real binaries
and temp files — no mocks. Cross-platform coverage comes from the
GitHub Actions OS matrix, not mocked process.platform.

Co-Authored-By: claude-flow <ruv@ruv.net>
Replace bare await import(serverPath) with importModule() to fix
Windows ESM loader error (Received protocol 'c:'). Replace
execFileSync with execBinary() for .cmd shim support on Windows.

Co-Authored-By: claude-flow <ruv@ruv.net>
Replace execFileSync/spawn with execBinary/spawnBinary from
@open-code-review/platform for Windows .cmd shim compatibility.
spawnBinary also sets windowsHide to prevent console flash on
detached workflow processes.

Co-Authored-By: claude-flow <ruv@ruv.net>
Move dashboard dist copy from project.json shell commands (rm -rf,
cp -r) into build.mjs using Node.js fs.rmSync/fs.cpSync. Removes
the separate build:copy-dashboard target.

Co-Authored-By: claude-flow <ruv@ruv.net>
Install @nx/vitest and @vitest/coverage-v8. Migrate all packages
from nx:run-commands to @nx/vitest:test executor with proper output
caching. Add tsconfig.spec.json and v8 coverage config to each
package. Remove unused @nx/jest dependency.

Co-Authored-By: claude-flow <ruv@ruv.net>
/api/health was registered after the bearer token middleware,
requiring auth for a health check. Move it above the middleware
so it's accessible without a token — needed for readiness probes
and e2e test harness.

Co-Authored-By: claude-flow <ruv@ruv.net>
New cli-e2e Nx project that spawns the built CLI binary as a real
subprocess. Tests: --version, --help, init, doctor, state show,
and unknown subcommand handling. Includes shared helpers for
spawning the CLI and creating temp project directories.

Co-Authored-By: claude-flow <ruv@ruv.net>
New dashboard-e2e Nx project that starts the built dashboard server
and tests API endpoints. Tests: health check (no auth), token
bootstrap, auth enforcement, sessions, stats, commands, and
reviewers endpoints. Server harness handles temp project setup,
port allocation, and cleanup.

Co-Authored-By: claude-flow <ruv@ruv.net>
Two workflows:
- ci.yml: automated on push/PR — unit tests on Ubuntu, then e2e
  smoke tests on Ubuntu + macOS + Windows matrix
- cross-platform-test.yml: manual workflow_dispatch — pick an OS
  and optionally open an SSH debug session via tmate

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch 3 times, most recently from a2d33f4 to c59d3f5 Compare April 3, 2026 08:07
spencermarx and others added 3 commits April 3, 2026 10:43
Replace concurrently + sleep 2 with scripts/dev.ts that starts the
server, polls for the port file (with mtime freshness check), then
starts Vite with PORT env var — eliminating the race entirely.

Also: server deletes stale port file on startup before binding, and
vite.config.ts prioritizes PORT env var for proxy target.

Co-Authored-By: claude-flow <ruv@ruv.net>
Rename dashboard-e2e → dashboard-api-e2e to distinguish from
upcoming UI e2e tests. Rename test files to semantic names:
- cli-smoke.test.ts → cli-commands.test.ts
- dashboard-smoke.test.ts → server-api.test.ts

Update root package.json scripts to match new project names.

Co-Authored-By: claude-flow <ruv@ruv.net>
New dashboard-ui-e2e Nx project using @nx/playwright for browser-
level regression tests. Includes:
- auth-token-proxy.spec.ts: verifies /auth/token returns JSON
  through the Vite proxy (regression for "Unexpected token '<'" bug)
- page-load.spec.ts: verifies the dashboard renders without fatal
  console errors

Also updates dashboard-api-e2e server harness with stalePort option
and port discovery regression test.

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch from c59d3f5 to 60b1aa4 Compare April 3, 2026 08:44
Restructure CI into three job types:
- test: unit tests on Ubuntu
- e2e-api: CLI + dashboard API smoke tests on 3-OS matrix
- e2e-ui: Playwright browser tests on Ubuntu only

Use pnpm exec for Playwright install in CI for deterministic
monorepo resolution. Install @nx/playwright + @playwright/test.

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch 4 times, most recently from f10e814 to 3e6d208 Compare April 3, 2026 09:01
Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch from 3e6d208 to 73b706e Compare April 3, 2026 09:05
@spencermarx
Copy link
Copy Markdown
Owner Author

Code Review: PR #24 — Windows compatibility + cross-platform e2e testing harness

Date: 2026-04-03
Reviewers: @principal-1, @Quality-1, @architect-1, @infrastructure-1, @testing-1
Mode: Full (with discourse)


Verdict

REQUEST CHANGES

Two blockers must be resolved: post-handler.ts still uses raw execFile for gh CLI calls (the exact pattern this PR eliminates), and CI jobs have no timeout-minutes setting (6-hour default on expensive macOS runners). The core platform fix is sound, the testing infrastructure is excellent, and the remaining issues are addressable without major rework.


Blockers

1. post-handler.ts bypasses platform lib for gh invocations

Flagged by: @principal-1
Type: Incomplete migration
Location: packages/dashboard/src/server/socket/post-handler.ts:9,23,62,125,491

Issue: post-handler.ts imports raw execFile from node:child_process and calls the gh CLI binary in three places (auth check, PR lookup, comment posting) without shell: true. On Windows, gh installed via npm is a .cmd shim — execFile('gh', ...) without shell: true will fail identically to the original dashboard crash this PR fixes.

Why this blocks: The PR's stated goal is to route all binary execution through the platform lib. This file was missed, leaving Windows gh posting broken.

Suggested fix: Add execBinaryAsync to @open-code-review/platform (since these calls run in the async Socket.IO server context where execFileSync would block the event loop), then migrate the three execFileAsync('gh', ...) calls:

// packages/shared/platform/src/index.ts — add:
export async function execBinaryAsync(
  binary: string,
  args: string[],
  opts?: ExecFileOptions,
): Promise<{ stdout: string; stderr: string }> {
  return execFilePromise(binary, args, { ...opts, shell: isWindows });
}

2. No job-level timeout in CI workflows

Flagged by: @infrastructure-1
Type: Operational risk
Location: .github/workflows/ci.yml:17-61 (all jobs), .github/workflows/cross-platform-test.yml

Issue: No CI job has a timeout-minutes setting. GitHub Actions defaults to 6 hours. If a test harness hangs (health poll never succeeds, server deadlocks), the CI job blocks an expensive runner for the full window.

Why this blocks: A single hung job on a macOS runner costs ~6x a Linux job and occupies a concurrent runner slot. This is a preventable operational risk.

Suggested fix: Add timeout-minutes to every job:

jobs:
  test:
    timeout-minutes: 20
    # ...
  e2e-api:
    timeout-minutes: 30
    # ...
  e2e-ui:
    timeout-minutes: 30
    # ...

Should Fix

1. execBinary return type is a type lie

Flagged by: @principal-1, @Quality-1, @architect-1
Location: packages/shared/platform/src/index.ts:46-49

The function declares string as the return type but execFileSync returns string | Buffer depending on the encoding option. The as string cast suppresses the type error without enforcing that callers pass encoding. A future caller omitting encoding will get a Buffer at runtime with no TypeScript warning. All current callers pass encoding: 'utf-8' — make this explicit in the signature via overloads or by requiring encoding: BufferEncoding in the options type.

2. Stale-port regression test has a race condition

Flagged by: @Quality-1, @testing-1
Location: packages/dashboard-api-e2e/src/server-api.test.ts:106

The 500ms setTimeout is a timing assumption. On a heavily loaded CI runner, 500ms may not be enough for the fork to start — the assertion vacuously passes. On a fast machine, the server may already be healthy — the stale-value check is also vacuous. Replace with a polling loop or IPC-based synchronization.

3. Playwright webServer requires .ocr/ init in CI

Flagged by: @infrastructure-1
Location: packages/dashboard-ui-e2e/playwright.config.ts:13-18

The webServer.command (npx nx dev dashboard) runs scripts/dev.ts which exits immediately if no .ocr/ directory exists. In CI (fresh clone), the e2e-ui job has no setup step to create this structure. Playwright will spend 2 minutes polling a dead process before failing with a confusing timeout. Add a CI setup step that creates the .ocr/ structure before the Playwright e2e job.

4. stdio: 'pipe' in test harness can deadlock on Windows

Flagged by: @infrastructure-1
Location: packages/dashboard-api-e2e/src/helpers/server-harness.ts:75

The forked server is started with stdio: 'pipe' but the pipe is never drained. On Windows, a full pipe buffer (64KB) will cause the child process to block, making waitForHealth time out because the server is deadlocked — not because it's unhealthy. Use stdio: process.env.CI ? 'inherit' : 'pipe' or drain the pipes explicitly.

5. Port counter collision risk in test harness

Flagged by: @infrastructure-1, @testing-1
Location: packages/dashboard-api-e2e/src/helpers/server-harness.ts:34

portCounter randomizes across a 1000-port range. Concurrent CI runs (matrix jobs, re-runs) can collide. EADDRINUSE will surface as a confusing 15s health timeout. Use OS port 0 allocation or add a pre-flight port check with retry.

6. spawnCli timeout/error handling masks failures

Flagged by: @Quality-1, @testing-1
Location: packages/cli-e2e/src/helpers/spawn-cli.ts:40-47

The err as { ... } cast and e.code ?? 1 fallback make timeouts, permission errors, and genuine exit-1 failures indistinguishable. Check e.killed for timeouts and throw descriptively. Add an existence check for CLI_BIN before tests run.

7. Ghost packages/dashboard-e2e/ directory

Flagged by: @architect-1
Location: packages/dashboard-e2e/

The old pre-split e2e directory with only node_modules/ matches the workspace glob. Delete it to prevent install warnings and nx show projects confusion.


Suggestions

Code Quality

  • "Use type instead of interface for CliResult and TempProject — project convention" — @Quality-1
  • "Extract duplicated git repo init (4 commands, 3 locations) into a shared helper" — @Quality-1, @testing-1
  • "Replace page.waitForTimeout(2_000) with a condition-based wait — Playwright anti-pattern" — @Quality-1, @testing-1, @infrastructure-1
  • "Add expect(result.stdout).toContain('No active session') to state show test" — @testing-1

Architecture

  • "Remove ../shared/platform/src/**/*.ts from dashboard tsconfig.json include — bypasses non-buildable boundary" — @architect-1
  • "Add explicit devDependencies to e2e package.json files so nx graph can trace them" — @architect-1

Infrastructure

  • "Consider running 3-OS matrix only on push to main, ubuntu-only for PRs — macOS runners ~10x cost" — @infrastructure-1
  • "Add comment explaining FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 env var purpose" — @infrastructure-1

Consensus & Dissent

Topic: execBinary return type

Reviewers: @principal-1, @Quality-1, @architect-1

Reviewer Position
@principal-1 "Use overloads or constrained generic"
@Quality-1 "Require encoding: BufferEncoding in options type"
@architect-1 "Require it — contract should be honest from day one"

Consensus: All agree the as string cast must be fixed. Preferred approach: require encoding in the type signature.

Topic: Playwright waitForTimeout

Reviewers: @Quality-1, @testing-1, @infrastructure-1
Consensus: All agree this should be replaced with a condition-based wait.

Topic: Dashboard tsconfig.json direct include

Reviewers: @architect-1
Context: The ../cli/src/lib/db/**/*.ts include already existed on the same line — the pattern predates this PR. Removing the platform include without investigating the root cause could break type-checking.
Resolution: Valid concern; investigate and fix in follow-up rather than blocking this PR.


What's Working Well

  • "Platform lib is a model of focused, well-documented utility code. The JSDoc explains not just what but why." — @Quality-1
  • "Non-buildable pattern is correctly implemented. Canonical Nx approach." — @architect-1
  • "The packages/shared/ directory is an extensible pattern for future shared code." — @architect-1
  • "Port-file race fix is well-engineered — mtime guard, 30s timeout, clear error." — @principal-1, @Quality-1
  • "Health endpoint correctly placed above auth middleware for readiness probes." — @infrastructure-1
  • "Detroit school compliance is strong — no mocks of internal code anywhere." — @testing-1
  • "The stale-port regression test is creative and valuable (despite the timing fragility)." — @testing-1
  • "Detection pattern in adapters is clean and uniform across all consumers." — @principal-1

Requirements Assessment

Requirement Status Notes Reviewer
Fix ESM loader Received protocol 'c:' ✓ Met importModule() wraps pathToFileURL @principal-1
Fix .cmd shim execution ⚠ Partial execBinary/spawnBinary done, but post-handler.ts missed @principal-1
Cross-platform build ✓ Met fs.rmSync/fs.cpSync replaces rm -rf/cp -r @Quality-1
E2e tests for CLI ✓ Met 7 CLI smoke tests @testing-1
E2e tests for dashboard ✓ Met 8 API tests + 2 Playwright UI tests @testing-1
CI with cross-platform matrix ✓ Met 3-OS matrix in ci.yml @infrastructure-1
Manual debug workflow ✓ Met cross-platform-test.yml with SSH @infrastructure-1

Gaps identified: 1 requirement partially met (post-handler.ts migration incomplete)


Clarifying Questions

Every question below was raised by a reviewer. Please address each.

From @principal-1

  • "Was post-handler.ts intentionally excluded from the migration, or is this an oversight?"
  • "Is execBinary sync-only by design, or was execBinaryAsync not added yet?"

From @architect-1

  • "Why does dashboard/tsconfig.json include platform sources directly via glob? Is there a build constraint?"
  • "Is packages/dashboard-e2e/ intentionally preserved?"

From @infrastructure-1

  • "Has pnpm nx e2e dashboard-ui-e2e been run successfully end-to-end in CI? What provides the .ocr/ directory?"
  • "Is there a plan to enable Nx remote caching?"

From @testing-1

  • "Should ocr state show exit non-zero when no session exists, or is exit 0 intentional?"
  • "Why not use port 0 for OS-assigned ports in the test harness?"

Individual Reviews

Full reviews available in session directory:

Reviewer Blockers Should Fix Suggestions File
@principal-1 1 2 4 rounds/round-1/reviews/principal-1.md
@Quality-1 0 4 6 rounds/round-1/reviews/quality-1.md
@architect-1 0 2 4 rounds/round-1/reviews/architect-1.md
@infrastructure-1 2 4 3 rounds/round-1/reviews/infrastructure-1.md
@testing-1 0 4 6 rounds/round-1/reviews/testing-1.md

Session: .ocr/sessions/2026-04-03-hotfix-issue-23-windows-and-cross-platform-compatibility/
Discourse: rounds/round-1/discourse.md

spencermarx and others added 2 commits April 3, 2026 11:31
Add async counterpart of execBinary for non-blocking binary execution
(needed by post-handler.ts Socket.IO context). Require encoding in
both sync and async variants to eliminate the as-string type lie.

Migrate post-handler.ts gh CLI calls from raw execFile to
execBinaryAsync — the last file using unshelled binary execution.

Co-Authored-By: claude-flow <ruv@ruv.net>
- spawnCli: add CLI_BIN existence check, CliTimeoutError class
- temp-project: use type instead of interface (project convention)
- server-harness: stdio: ignore to prevent Windows pipe deadlock
- auth-token-proxy: replace waitForTimeout with waitForFunction
- cli-commands: add state show output assertion
- All e2e packages: add explicit devDependencies for nx graph tracing

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch from de17f6d to 46041b1 Compare April 3, 2026 09:35
- timeout-minutes on all jobs (20 unit, 30 e2e) to prevent 6h hangs
- 3-OS matrix on push to main only; ubuntu-only for PRs (cost savings)
- OCR directory + git identity init step before Playwright tests
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 env var with explanatory comment

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx force-pushed the hotfix/issue-23-windows-and-cross-platform-compatibility branch from 46041b1 to 6e16c5b Compare April 3, 2026 09:36
@spencermarx
Copy link
Copy Markdown
Owner Author

Code Review: PR #24 — Windows compatibility + cross-platform e2e testing harness

Date: 2026-04-03
Reviewers: @principal-1, @Quality-1, @architect-1, @infrastructure-1, @testing-1
Mode: Full (with discourse)
Round: 2


Verdict

APPROVE

No blockers identified. The PR correctly fixes the Windows ESM loader crash (issue #23) and introduces a well-designed cross-platform testing infrastructure. Seven should-fix items are recommended for follow-up — none block merge.


Blockers

None.


Should Fix

1. dev.ts uses raw spawn instead of spawnBinary

Flagged by: @principal-1, @Quality-1, @architect-1
Location: packages/dashboard/scripts/dev.ts:11-86

The dev script imports spawn directly from node:child_process and passes shell: true unconditionally, rather than using spawnBinary from @open-code-review/platform. This breaks the centralization pattern this PR establishes. The platform lib exists to be the single gateway for cross-platform process spawning — having the dev script bypass it weakens the architectural invariant and introduces unnecessary shell injection surface on POSIX.

Fix: Import spawnBinary from @open-code-review/platform.

2. dev.ts cleanup can fire twice

Flagged by: @Quality-1
Location: packages/dashboard/scripts/dev.ts:93-109

Both server.on("exit") and client.on("exit") call cleanup(), which kills both processes. Signal handlers (SIGINT, SIGTERM) also call cleanup(). This cascading cleanup can call process.exit() multiple times, potentially producing confusing exit codes on some platforms.

Fix: Add a let shuttingDown = false guard at the top of cleanup().

3. execBinaryAsync has zero test coverage

Flagged by: @testing-1, @principal-1
Location: packages/shared/platform/src/__tests__/platform.test.ts

The platform library exports 4 functions but only 3 are tested. execBinaryAsync is used in production (post-handler.ts for GitHub CLI operations) with zero unit test coverage. The testing pattern for the other 3 functions is already established — this should be consistent.

Fix: Add a describe("execBinaryAsync") block with happy-path and error-path tests.

4. Stale port file test relies on hardcoded 500ms sleep

Flagged by: @testing-1
Location: packages/dashboard-api-e2e/src/server-api.test.ts:106

The test waits 500ms and assumes the server's synchronous init has completed. On slow CI runners (especially Windows GitHub Actions), the process may not have started within 500ms. This timing-dependent test is a flaky test risk.

Fix: Replace the sleep with a polling loop that checks until the port file changes from 9999 or disappears, with a reasonable timeout (e.g., 5s).

5. SSH debug workflow has no timeout guard

Flagged by: @infrastructure-1
Location: .github/workflows/cross-platform-test.yml:50-54

The tmate SSH step holds the runner open until manual disconnect or the 30-minute job timeout. A forgotten macOS debug session costs the equivalent of 300 Linux runner minutes (10x cost multiplier).

Fix: Add timeout-minutes: 15 to the tmate step.

6. No artifact upload on e2e failure

Flagged by: @infrastructure-1
Location: .github/workflows/ci.yml:33-71

When e2e tests fail, no logs, screenshots, or Playwright traces are preserved. Playwright is configured with trace: "on-first-retry" but the trace directory is never uploaded. Diagnosing Windows-specific failures requires the expensive SSH debug workflow.

Fix: Add actions/upload-artifact@v4 with if: failure() for e2e jobs.

7. Dashboard vitest.config.ts hardcodes path to platform source

Flagged by: @architect-1
Location: packages/dashboard/vitest.config.ts:12

The vitest config manually aliases @open-code-review/platform with a hardcoded relative path, duplicating the resolution already in tsconfig.base.json. If the platform lib's structure changes, this alias breaks silently in tests while TypeScript compilation continues.

Fix: Investigate using vite-tsconfig-paths plugin, or document this as the standard pattern.


Suggestions

Architecture & Patterns

  • "No @nx/enforce-module-boundaries ESLint rule — tags are decorative without it. Consider adding enforcement as a follow-up." — @architect-1
  • "packages/shared/ introduces a new depth level with only one package. Fine at current scale, but monitor for anti-pattern growth." — @architect-1
  • "Consider extracting duplicated git setup logic from test harnesses into a shared helper." — @Quality-1

CI & Infrastructure

  • "CI e2e-api job runs cli-e2e and dashboard-api-e2e sequentially — if CLI fails, API results are hidden. Consider run-many or continue-on-error." — @principal-1
  • "Playwright UI e2e runs only on Ubuntu. No automated UI regression testing on Windows. Document the decision." — @infrastructure-1
  • "Concurrency group cancel-in-progress: true may cancel 10-30 min e2e runs on rapid pushes. Consider splitting concurrency groups." — @infrastructure-1

Code Quality

  • "Magic numbers for timeouts (15000, 30000) and port ranges (14000) should be named constants." — @Quality-1
  • "build.mjs remains a .mjs file despite project convention requiring TypeScript. Pre-existing but touched in this PR." — @Quality-1
  • "Remove concurrently from dashboard devDependencies — dead dependency after dev.ts replacement." — @principal-1

Testing

  • "Server harness cleanup should send SIGKILL after 5-second timeout if SIGTERM is ignored." — @testing-1
  • "Playwright auth test silently catches window.__OCR_TOKEN__ failures. Test degrades without failing if the global is renamed." — @testing-1
  • "Add boundary tests for importModule with paths containing spaces — common on Windows." — @testing-1
  • "e2e test harnesses use raw execFileSync for git instead of execBinary. Functional but inconsistent." — @principal-1, @Quality-1

Informational

  • "Port allocation uses 14000 + random(0-999). Acceptable now with separate CI runners per OS. Switch to port 0 if moving to self-hosted." — @testing-1, @infrastructure-1
  • "process.kill(-pid) in server/index.ts is POSIX-only — pre-existing code, not introduced by this PR. Flag for future Windows hardening." — @infrastructure-1
  • "Server harness fork() + child.kill() becomes hard kill on Windows (no graceful shutdown). Pre-existing pattern." — @infrastructure-1

Consensus & Dissent

Topic: dev.ts bypasses platform lib

Reviewers: @principal-1, @Quality-1, @architect-1, @infrastructure-1

Reviewer Position
@principal-1 "Breaks centralization pattern. Should use spawnBinary."
@Quality-1 "Inconsistent with PR's own rationale. Unnecessary shell surface on POSIX."
@architect-1 "Weakens architectural invariant. Low priority for dev-only script."
@infrastructure-1 "Harmless for hardcoded commands, but pattern should be uniform."

Consensus: All agree — use spawnBinary for consistency.

Topic: Test harness duplication

Reviewers: @Quality-1, @architect-1, @testing-1

Reviewer Position
@Quality-1 "Extract shared createTempGitRepo() helper."
@architect-1 "Shared test utility package warranted at two packages."
@testing-1 "Some duplication in e2e is acceptable. Prefer local refactoring."

Consensus: Partial — extraction preferred but not urgent. Author's discretion.


What's Working Well

  • "Platform lib design is excellent — minimal contract surface, four focused functions, enforced encoding in type signatures. Impossible to misuse." — @principal-1, @architect-1
  • "Non-buildable workspace lib pattern correctly implemented. No redundant build step, consumers inline source via bundlers." — @architect-1
  • "Detroit school testing philosophy faithfully applied throughout. Real processes, real HTTP, real filesystems. No mocks." — @testing-1, @Quality-1
  • "Dev proxy race condition fix with port-file polling and timestamp-based freshness check is significantly more robust than the previous sleep 2 approach." — @principal-1, @Quality-1
  • "Conditional 3-OS CI matrix (push = full, PR = ubuntu-only) is a smart cost optimization." — @infrastructure-1, @principal-1
  • "JSDoc comments explain why each function exists, not just what it does." — @Quality-1
  • "Release configuration correctly excludes shared and e2e packages from publishing." — @architect-1
  • "The existsSync guard in spawn-cli.ts fails fast with a clear error message — excellent DX." — @testing-1
  • "Port discovery regression test targets the exact race condition from the original bug." — @testing-1

Requirements Assessment

Requirement Status Notes Reviewer
Fix ESM loader Received protocol 'c:' ✓ Met importModule() wraps pathToFileURL @principal-1
Fix .cmd shim execution on Windows ✓ Met execBinary/spawnBinary set shell: isWindows @principal-1
Replace Unix-only shell commands ✓ Met fs.rmSync/fs.cpSync in build script @Quality-1
E2e smoke tests for CLI ✓ Met 7 CLI test cases @testing-1
E2e smoke tests for dashboard API ✓ Met 8 API test cases @testing-1
Playwright browser tests for UI ✓ Met 4 Playwright tests @testing-1
CI with cross-platform OS matrix ✓ Met Conditional 3-OS matrix on push @infrastructure-1
Manual cross-platform testing workflow ✓ Met workflow_dispatch with SSH debug @infrastructure-1

All requirements met. No gaps identified.


Clarifying Questions

From @principal-1

  • "Does esbuild resolve the platform lib source correctly through tsconfig paths when bundling the dashboard server?"
  • "Is there a reason concurrently was kept in devDependencies?"

From @architect-1

  • "CLI lists @open-code-review/platform under dependencies, dashboard under devDependencies. Intentional for Nx graph resolution, or incidental?"

From @infrastructure-1

  • "Is this repository on a GitHub plan with free macOS/Windows minutes, or are those billed?"
  • "Is FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 a real GitHub env var? Could not find it in docs."

From @testing-1

  • "Should platform unit tests also run on the 3-OS matrix, or is e2e coverage sufficient?"

Individual Reviews

Full reviews available in session directory:

Reviewer Should Fix Suggestions File
@principal-1 2 6 rounds/round-2/reviews/principal-1.md
@Quality-1 3 6 rounds/round-2/reviews/quality-1.md
@architect-1 1 4 rounds/round-2/reviews/architect-1.md
@infrastructure-1 4 4 rounds/round-2/reviews/infrastructure-1.md
@testing-1 2 6 rounds/round-2/reviews/testing-1.md

Session: .ocr/sessions/2026-04-03-hotfix-issue-23-windows-and-cross-platform-compatibility/
Discourse: rounds/round-2/discourse.md

spencermarx and others added 2 commits April 3, 2026 11:56
- dev.ts: use spawnBinary instead of raw spawn, add cleanup guard
- platform tests: add execBinaryAsync behavioral test coverage
- server-api.test: replace 500ms sleep with polling loop for stale
  port file check (prevents flake on slow CI runners)
- Remove dead concurrently devDependency from dashboard

Co-Authored-By: claude-flow <ruv@ruv.net>
Upload Playwright report/traces on e2e-ui failure for debugging
without SSH. Add 15-minute timeout to tmate SSH debug step to
prevent forgotten macOS sessions from burning runner minutes.

Co-Authored-By: claude-flow <ruv@ruv.net>
@spencermarx spencermarx merged commit a2a626a into main Apr 3, 2026
3 checks passed
@spencermarx spencermarx deleted the hotfix/issue-23-windows-and-cross-platform-compatibility branch April 3, 2026 12:58
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.

Dashboard fails on Windows: ESM loader rejects Windows absolute paths

1 participant