debug-windows-on-parallels
1lck/Lithe-IDEA/.agents/skills/debug-windows-on-parallels/SKILL.md
Build, run, and diagnose the Windows Tauri product from a macOS host through a Parallels Windows VM. Use when a task requires building windows/, reproducing Windows-only behavior, reading Windows application or JDTLS logs, or transferring files into the Windows VM.
Skill1.9k starsChanged yesterday
What's in it
- Debug Windows on Parallels
- Own the VM lifecycle and shut it down after validation
- Reach the VM through prlctl, not SSH
- Transfer files over the Parallels shared folder
- Build through a guest .cmd wrapper
- Read Windows-side diagnostics
- Match the diagnosis to the layer
- Search tools in this environment
---
name: debug-windows-on-parallels
description: Build, run, and diagnose the Windows Tauri product from a macOS host through a Parallels Windows VM. Use when a task requires building windows/, reproducing Windows-only behavior, reading Windows application or JDTLS logs, or transferring files into the Windows VM.
---
# Debug Windows on Parallels
The Windows product cannot be built or exercised on macOS directly. This Skill
covers the host-to-guest workflow: what channel to use, which failures are
environmental rather than code defects, and where Windows-side diagnostics live.
Only load this Skill when the task actually reaches into the VM. Editing
`windows/` source and running `verify-windows-boundaries.sh` on macOS needs
nothing from here.
## Own the VM lifecycle and shut it down after validation
A running VM consumes substantial host CPU and memory even after a build or
test process exits. Closing its window or stopping Cargo is not VM cleanup.
- Before starting or resuming the VM, record its name and initial state with
`prlctl list -a`. Keep it stopped during host-only coding and inspection.
- If this task starts or resumes the VM, shut it down when the Windows
validation session ends, fails, is cancelled, or is paused to resume coding.
Do not leave it running until the overall development task is complete or
merely because another Windows check may be needed later. The exception is
an explicit user request to keep it running, such as for manual testing.
- First stop task-owned builds, tests, Lithe instances, JDTLS, and helper
processes, and collect needed logs. Then request a normal guest shutdown:
```bash
prlctl stop "<vm-name>"
prlctl list -a
```
- Wait for the shutdown operation and confirm the VM reports `stopped` before
reporting cleanup complete. Use bounded observation; if shutdown fails or
stalls, inspect and report the remaining state. Do not silently claim success
or force-kill the VM and risk guest data loss.
- If the VM was already running for the user before this task, clean up only
task-owned processes and preserve that initial state unless the user asks
for shutdown. Record any VM intentionally left running and why.
## Reach the VM through prlctl, not SSH
`prlctl` is the only reliable channel. SSH into the guest requires an
interactive password and cannot be scripted.
```bash
prlctl list # names and run state
prlctl exec "<vm-name>" cmd /c "..."
prlctl exec "<vm-name>" powershell -Command "..."
```
`prlctl exec` defaults to `NT AUTHORITY\\SYSTEM`. That context does not have
the interactive user's PATH, Rustup home, Bun installation, Git config, or
mapped drives. For product builds and user-session diagnostics, use the
interactive Windows account instead:
```bash
prlctl exec "<vm-name>" --current-user cmd /c "..."
prlctl exec "<vm-name>" --current-user powershell -Command "..."
```
Verify the context before running a build:
```bash
prlctl exec "<vm-name>" --current-user cmd /c "whoami & where rustup & where bun"
```
Use the default SYSTEM form only for machine-level inspection or actions that
do not depend on the logged-in user's tools and configuration. The
`--current-user` form is required for Windows builds, Git behavior, and logs
produced by the active Lithe session.
`prlctl exec` runs as `NT AUTHORITY\SYSTEM`, not the logged-in user. Three
consequences follow, and all three look like "the tool is not installed":
- The user `PATH` is invisible. `bun`, `cargo`, `rustup`, and `node` all appear
missing. Invoke them by absolute path, or set `PATH` inside a `.cmd` wrapper.
- `git` reports `detected dubious ownership` because the checkout belongs to the
interactive user. Pass `-c safe.directory=<path>` or configure it globally in
the guest once.
- `winget` is unavailable. Install guest tooling interactively instead.
PowerShell in the guest emits localized text that arrives as mojibake on the
macOS side. Match on timestamps, severities, and English identifiers; do not try
to read localized error prose.
## Transfer files over the Parallels shared folder
The macOS home directory is mounted in the guest at `\\psf\Home`. Copy through
it in a single call:
```bash
prlctl exec "<vm-name>" powershell -Command "
Copy-Item '\\\\psf\\Home\\<path-under-home>' 'C:\<guest-path>' -Force
"
```
Use `\\psf\Home`, not `\\Mac\Home`; the latter is not resolvable from the
`SYSTEM` context.
Never chunk file contents through repeated `prlctl exec` calls (for example
base64 in a shell loop). Every call spawns a guest PowerShell process; a handful
of files is enough to exhaust guest resources, stall the host, and generate
large amounts of disk writes. If a shared folder is genuinely unavailable, stop
and ask rather than looping.
## Build through a guest .cmd wrapper
Write a `.cmd` file in the guest that establishes the environment, then run it
detached with output redirected to a log:
```cmd
@echo off
set "PATH=C:\Program Files\LLVM\bin;C:\Users\<user>\.cargo\bin;C:\Users\<user>\.bun\bin;%PATH%"
set "CARGO_HOME=C:\Users\<user>\.cargo"
set "RUSTUP_HOME=C:\Users\<user>\.rustup"
cd /d C:\<repo>
powershell -NoProfile -ExecutionPolicy Bypass -File ".\scripts\build-windows.ps1" -Configuration Debug -RustTarget aarch64-pc-windows-msvc
echo === exit: %ERRORLEVEL% ===
```
```bash
prlctl exec "<vm-name>" cmd /c "start /min cmd /c \"C:\\<build>.cmd > C:\\<build>.log 2>&1\""
```
Driving the build from `cmd` with `cmd`-level redirection is deliberate.
`build-windows.ps1` sets `$ErrorActionPreference = "Stop"`, and `rustup` and
`bun` write progress to stderr; under PowerShell redirection those become
terminating `NativeCommandError`s and the build fails while the underlying
command succeeded. A build that dies immediately after a tool prints a normal
informational line is this, not a real failure — check the tool standalone
before treating it as a code defect.
Other environmental failures worth recognizing:
- **Wrong target.** On Apple Silicon the guest is ARM64, so pass
`-RustTarget aarch64-pc-windows-msvc`. The script's default is x86_64.
- **Script execution blocked.** First run needs
`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned -Force` in the guest.
- **`Access is denied` removing the exe.** The app is still running. Stop it
first: `Get-Process -Name '<product>' -ErrorAction SilentlyContinue | Stop-Process -Force`.
- **Missing native toolchain.** Some crates require `clang`; the guest may have
LLVM installed without it being on the `SYSTEM` `PATH`. Add
`C:\Program Files\LLVM\bin` to the wrapper's `PATH`.
- **Shared crate edits not picked up.** Cargo compares mtimes, and a file
written through the shared folder can keep an older timestamp. Touch it:
`(Get-Item <path>).LastWriteTime = Get-Date`. Confirm the intended crate
actually recompiled by grepping the build log for its `Compiling` line.
- **Debug builds omit bundled language servers.** `build-windows.ps1` stages
them only for Release. Mirror them into the Debug profile when a Debug build
must exercise language features:
`robocopy <target>\release\LanguageServers <target>\debug\LanguageServers /E /NFL /NDL /NJH /NP`.
## Read Windows-side diagnostics
Application logs live under the product's `LOCALAPPDATA` directory; the most
recently written file is the current session.
```bash
prlctl exec "<vm-name>" powershell -Command "
\$f = Get-ChildItem 'C:\Users\<user>\AppData\Local\<bundle-id>\logs\' |
Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName
Select-String -Path \$f -Pattern 'error' | Select-Object -Last 20 | ForEach-Object { \$_.Line }
"
```
Know what the log will and will not show before concluding a feature is broken.
Per `windows/tauri/src-tauri/src/logging.rs`:
- `Debug`-level records are dropped unless diagnostic logging is enabled for the
session (Settings → Logs). Enable it *before* reproducing, or the most useful
records will not exist.
- The sanitizer rewrites absolute paths to `<redacted_path>` and redacts field
values whose key looks credential-like. Keys containing `token` are caught by
this, so some benign fields read as `<redacted>`.
- Lines are truncated at 4 KB, which cuts the tail off long React component
stacks.
- Only `Warn` and above are flushed synchronously, so a hard crash can lose the
last few `Info` records.
Frontend stack traces from a production build are minified. Inline source maps
do **not** fix this: V8 does not apply source maps when building
`Error.stack` — that is devtools-only behavior. Readable frames require devtools,
which needs the `devtools` feature on the `tauri` crate; a `--debug` build alone
does not enable it.
Language-server internals are not in the application log. JDTLS keeps its own
log at
`<LOCALAPPDATA>\<bundle-id>\language-servers\jdtls\<hash>\.metadata\.log`.
Maven import results, classpath resolution, and index read/write failures appear
only there. A `ProjectRegistryRefreshJob finished` time in single-digit
milliseconds means no build system was actually imported.
## Match the diagnosis to the layer
Before changing product code, place the symptom:
- **Guest environment** — missing `PATH`, ownership, execution policy, target
triple, locked exe. Fix the harness, not the product.
- **Build staging** — a resource the Debug profile never received. Check what
`build-windows.ps1` stages per configuration.
- **Durable language-server state** — JDTLS keeps per-workspace state that it
never invalidates itself. A workspace whose structure changed can keep
resolving against a stale project model, which presents as navigation and
highlighting working for some files and not others.
- **Product defect** — only after the three above are excluded.
State which layer the evidence supports. When a check could not run on the
current machine, say so rather than implying it passed.
## Search tools in this environment
`grep` is proxied and does not behave like GNU grep for absolute paths or `\|`
alternation. Prefer `rg -n "a|b"` with an unescaped pipe, or run separate
patterns.
More agent context in 1lck/Lithe-IDEA
12 other files this repository gives its agents.
AGENTS.md
CLAUDE.md
Skill
- agent-notes.agents/skills/agent-notes/SKILL.md
- develop-lithe.agents/skills/develop-lithe/SKILL.md
- lithe-code-review.agents/skills/lithe-code-review/SKILL.md
- release-lithe.agents/skills/release-lithe/SKILL.md
- write-stable-tests.agents/skills/write-stable-tests/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
Reports can't be read right now.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.

