| name | build-windows-tray |
| description | Build + install the windows-tray release binary on the local Windows host, post-install --diagnose verify, and file findings in ./plan/ for future fixing. |
/build-windows-tray
A canonical, fine-tunable workflow for the windows host's daily build+install
loop (scheduled via /loop 24h). Treat this file as the source of truth; tune it
between iterations as the build flow evolves.
Why this exists
Cargo+install can only run on the actual Windows host (cloud schedules can't
drive wsl.exe or write to %LOCALAPPDATA%). This skill encapsulates the
exact, known-working steps so they stay consistent across daily fires and so
the workflow is reviewable + improvable.
Working dir
C:/Users/bullo/src/tillandsias
Steps
Execute every step. Capture findings into a per-day file in plan/diagnostics/
named build-windows-tray-YYYY-MM-DD.md so other hosts can audit them.
1. Sync working tree
git fetch --all -q
B=$(git branch --show-current)
[ "$B" = "windows-next" ] || { echo "WRONG BRANCH: $B"; exit 1; }
git merge --ff-only origin/linux-next 2>&1 | tail -2
If FF fails (e.g. non-fast-forward because of local commits), DO NOT force
or rebase silently — note the divergence in the day's findings file and
continue building from current HEAD.
2. Release build
As of 2026-05-30, scripts/build-windows-tray.ps1 locally relaxes
$ErrorActionPreference around the cargo invocation, so its stderr writes
("Compiling …", "Finished …") no longer trip the Stop trap as
NativeCommandError. Either path works:
$env:Path = "$env:USERPROFILE\.cargo\bin;$env:Path"
# Either invoke the wrapper (preferred — same path users hit):
& scripts\build-windows-tray.ps1
# OR invoke cargo directly (still works, useful for ad-hoc):
cargo build -p tillandsias-windows-tray --release
Verify the artifact exists with a fresh LastWriteTime:
Get-ChildItem target\release\tillandsias-tray.exe |
Format-Table Name, Length, LastWriteTime -AutoSize
Expected size: ~6 MB (release-optimized GUI-subsystem binary).
3. Stop any running tray
Get-Process tillandsias-tray -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.Id -Force }
Start-Sleep -Seconds 2
The 2 s sleep is important — Windows can keep the .exe file handle briefly
after the process exits, and Copy-Item -Force will fail with IOException
("file in use") if you proceed immediately.
4. Install
As of 2026-05-30, scripts/install-windows.ps1 runs end-to-end (the
build-subscript stderr-wrap issue that previously aborted it mid-install was
fixed in the same 2026-05-30 build-windows-tray.ps1 tuning). Prefer the
script — it handles shortcut creation, autostart, and the Layer 1 (--version)
- Layer 2 (
--diagnose --json) post-install verification you'd otherwise
re-implement here:
& scripts\install-windows.ps1
The script rebuilds via the (now-safe) build subscript, copies to
%LOCALAPPDATA%\Programs\Tillandsias\tillandsias-tray.exe, creates a Start
Menu shortcut, and runs the two-layer post-install sanity check. Direct
copy is still fine if you want to skip the rebuild:
$installDir = Join-Path $env:LOCALAPPDATA 'Programs\Tillandsias'
New-Item -ItemType Directory -Force $installDir | Out-Null
Copy-Item target\release\tillandsias-tray.exe `
(Join-Path $installDir 'tillandsias-tray.exe') -Force
5. Post-install --diagnose sanity check
ALWAYS via cmd /c redirect to a file — PowerShell's direct stdout capture
of the release (GUI-subsystem) binary silently drops large println! writes
(see cheatsheets/runtime/windows-tray-diagnostics.md and commit
d7bfcdd9):
$installed = Join-Path $env:LOCALAPPDATA 'Programs\Tillandsias\tillandsias-tray.exe'
$tmp = "$env:TEMP\build-windows-tray-diag.json"
& cmd /c "`"$installed`" --diagnose --json > `"$tmp`" 2>nul"
$diagExit = $LASTEXITCODE
$report = Get-Content $tmp | ConvertFrom-Json
Remove-Item $tmp -ErrorAction SilentlyContinue
Expected diagExit:
0 — fully healthy (distro registered AND wire reachable AND phase Ready)
2 — degraded (binary works but VM is idled / not provisioned; expected
in the daily loop unless the VM was kept warm)
1 — hard failure (install bits broken — record + escalate)
6. Findings file
Mirrors macOS's /build-macos-tray ledger pattern (commit 0375ee1a): one
file per day, append a new section per run rather than overwrite. Path:
plan/issues/windows-build-findings-YYYY-MM-DD.md
Living in plan/issues/ (not plan/diagnostics/) so cross-host work-queue
greps see it. Sibling hosts grep SECTION_KIND to see whether the Windows
build is green on a given day.
If the file does not yet exist, prepend this header:
# Windows tray build findings — YYYY-MM-DD
Generated by `/build-windows-tray`. One section per build run. Sibling hosts
should grep `SECTION_KIND` to see whether the Windows build is green on a
given day; section bodies surface anything that affects cross-host work
(rare unattended regressions, install-script edge cases, smoke surprises).
trace: skills/build-windows-tray/SKILL.md (the skill that wrote this)
plan/steps/windows-next-thin-tray.md (the windows tray v0.0.1 ledger)
Then APPEND a section per run:
---
### YYYYMMDDTHHMMSSZ — <SECTION_KIND>
- agent_id: <agent_id e.g. windows-bullo-claude-opus-YYYYMMDDTHHMMSSZ>
- head_sha: <short SHA after FF>
- version: <workspace VERSION>
- build_run_id: YYYYMMDDTHHMMSSZ
**Build**:
- duration: <N s wall-clock>
- exe: target/release/tillandsias-tray.exe (<size> bytes)
**Autonomous smoke** (post-install diagnose):
- `--diagnose --json` exit: <0 | 2 | 1>
- `--diagnose --json` keys present: [<comma-list>] — all **16 schema keys**
per `litmus:windows-tray-diagnose-cli-surface` (count grows over time;
check the cheatsheet § `--diagnose --json` schema for the current list).
- version: <workspace VERSION from JSON>
- build_commit: <short SHA from JSON>
- install_path: <path from JSON>
- distro registered / running: <bool> / <bool>
- WSL / OS: <wsl_version> / <os_version>
- control wire reachable: <bool> (phase=<phase>, podman_ready=<bool>)
- last_event: <string | null>
**Install**:
- target: %LOCALAPPDATA%\Programs\Tillandsias\tillandsias-tray.exe
- backup made: <yes (prior bundle at <size>, <date>) | no — first install>
- post-install diagnose schema match: <yes | no — diff>
**Findings** (free-form; what regressed, what surprised, what to investigate):
- <one line per issue, reproducer if non-obvious>
**Cross-host visibility note**: <text | N/A (SECTION_KIND=ok)>
**Next iteration ask**: <text | N/A (SECTION_KIND=ok)>
SECTION_KIND values:
ok — Build + install + autonomous-smoke all green (exit 0 or 2 is ok; exit 1
is not).
regression — Something previously-green broke. Surface immediately.
degraded — Build/install green but smoke partial (e.g. wire unreachable
AND distro un-registered, where the latter would be unexpected).
7. Commit + push
git add plan/issues/windows-build-findings-YYYY-MM-DD.md
git commit -m "chore(windows-build-findings): YYYYMMDDTHHMMSSZ <SECTION_KIND>"
git push origin windows-next
8. Report
Print a 5-line summary back to the user covering: the FF outcome, build status,
install bytes/timestamp delta, --diagnose --json exit code + phase, and any
findings worth surfacing.
Tuning log
Edit this section over time. Each entry: date + what changed + why.
- 2026-05-29: initial. Direct cargo invocation (not the wrapper script).
Documents the PowerShell-stderr-wrap, copy-after-stop-sleep, and
cmd-redirect requirements drawn from the real loop's experience.
- 2026-05-30:
scripts/build-windows-tray.ps1 now locally relaxes
$ErrorActionPreference around the cargo call (with try/finally to
restore on exit), so cargo's stderr writes no longer trip the Stop trap.
The wrapper + scripts/install-windows.ps1 both run end-to-end without
the bypass. Skill body switched to prefer the script for builds + install
while still showing the direct-cargo path for ad-hoc use.
- 2026-05-31: large session of windows-tray surface enrichment
shipped — too much to list per-commit, but the steady-state v0.2.260531.1
era windows-tray now has: 7 CLI modes (
--provision-once,
--status-once [--json], --diagnose [--json], --logs [--tail N] [--bak],
--help, --version, GUI); 16-key DiagnoseReport (5 binary identity +
2 host versions + 4 host facts + 1 manifest pin + 1 wire sub-object +
1 log array, see cheatsheet for the full schema); 4 Win11 toasts
(provisioning success/failure + wire degraded/recovered); 4 operator
env vars (RUST_LOG, TILLANDSIAS_NO_PROVISION, BUILD_COMMIT_SHA_OVERRIDE,
workspace VERSION via build.rs); install-windows.ps1 grew -Purge
mode with wsl --unregister + cache cleanup; tray.log size-rotation
at 5 MiB threshold with 1-bak ring; cli_integration test suite
exercising the real binary end-to-end (4 tests in
tests/cli_integration.rs). Schema-key claim in section 6 above
bumped 10 → 16 to match; --diagnose JSON keys grow over time and
the cheatsheet is the source of truth for the current set. Section
6 template enriched with version / build_commit / install_path
rows reflecting the new schema. Duplicate ### 7. numbering fixed
(Report is ### 8. now). No daily-flow changes — install-windows.ps1
end-to-end already exercises everything.