Skip to content

Latest commit

 

History

History
182 lines (156 loc) · 10.6 KB

windows-port.md

File metadata and controls

182 lines (156 loc) · 10.6 KB

Proposal — Windows port (solo first)

Status: implemented, with the same memory as macOS — install.ps1 installs GBrain (PGLite + pgvector) and runs end to end on a real Windows in CI (windows job in .github/workflows/test.yml); field reports welcome (originally a proposal, per CONTRIBUTING: approach before code). Scope: make brain-in-a-box installable and runnable for a solo user on Windows. Team mode (setup-company.sh) is out of scope for v1.

Update 2026-10 — gbrain on Windows, the gate is open. gbrain #1549 (pgvector missing from PGLite on Windows) does not reproduce. A spike on four Windows runners (windows-latest, windows-2022, windows-11-arm, and windows-latest with Defender real-time scanning on), gbrain 0.60.210.0 and Bun 1.4.3, no secret:

  • pgvector 0.8.1 loads in gbrain's own PGLite build: CREATE EXTENSION, HNSW index and <=> distance on disk, under a path with a space and an accent too;
  • keyword brain (init --no-embedding): init, import, query and search in about 1 s, and reads exit on their own (the PGLite read hang that gbq works around on macOS does not happen here);
  • Voyage path (voyage:voyage-4, 1024 d, rerank-3-lite) against a local stand-in (.github/ci/fake_voyage.py, wired through provider_base_urls): embeddings written, the query by meaning ranks the right page first;
  • 1000 pages, the macOS nightly cycle (sync --no-embed, embed --stale --max-usd): sync 39-66 s (13 s on an M-series Mac), embed 15-23 s; a writer killed mid-sync leaves no corruption; an S4U scheduled task runs it all.

What ships, in install.ps1:

  • Bun from its official installer (per user, no admin, -NoCompletions) and GBrain at $GBRAIN_REF, a commit fetched alone (--depth 1, about 46 MB instead of 600 MB of history), then bun install + bun link. Both versions are pinned: gbrain moves daily, and the windows CI job installs exactly that pin, so bumping it is a PR whose CI must pass. Running the install line again moves a user to the new pin; self_upgrade.mode is set to off so gbrain upgrade never bypasses it. There is no nightly self-update on Windows.
  • Voyage key, optional, same text and one-token check as install.sh. Asked only when a console is there (irm | iex from Win+R); without one, VOYAGE_API_KEY from the process. The key goes to ~\.gbrain\config.json through Python's environment, never in a command line or the install log. A key given later moves an existing keyword brain to search by meaning.
  • The vault is a gbrain source (sources add vault --path <vault>), then sync --source vault. On a fresh gbrain the default source is gbrain's own content folder, so sync --repo <vault> without a registered source fails with "not a git repository" (macOS too). The vault is committed first: gbrain reads it through git, and Windows installs before this one never committed it.
  • gbq (gbq.cmd, and gbq for Git Bash, Claude Code's Bash tool) runs engine/search/gbq.py: gbrain when ~\.brain-search\engine says so; a busy brain (pglite_busy, a long write holds PGLite's single writer for more than 30 s) is retried once; a missing, broken or still-busy gbrain, or a read still running after 60 s (killed with its Bun process), is answered from brain_search, the offline keyword index, which is always installed and rebuilt. If gbrain cannot be installed at all, the install still succeeds in keyword mode.
  • BrainNightly (Task Scheduler 04:00, S4U, runs at wake-up when missed, on battery too): engine/nightly/windows-nightly.py commits the vault, runs sync and embed --stale --max-usd 0.10, then rebuilds the keyword index. It replaces BrainSearchReindex. No dream cycle, no weekly lint yet.

Not covered by CI: third-party antivirus, SmartScreen, a non-admin account, Windows Home, real traffic to api.voyageai.com from Bun (corporate proxy or TLS inspection), gbrain serve (MCP). First indexing is 3 to 5 times slower than on an M-series Mac: a large vault can take several minutes.

Update 2026-06 — Phase 1 done. The engine hooks are now cross-platform (pure Python): /tmp → tempfile.gettempdir(), claude resolved via shutil.which(), home resolved at runtime with Path.home() (no __HOME__ bake-in), and .gitattributes enforces LF so CRLF can't break the hooks on Windows. This benefits macOS too and is not gated by gbrain.

Update 2026-06 — Windows path now implemented (search un-gated). Rather than wait on gbrain #1549, the search is replaced on Windows by engine/search/brain_search.py — a local BM25 search in pure Python (no bun, no pgvector, no model download, offline). install.ps1 wires it up: copies the vault skeleton, installs the (now cross-platform) hooks, builds the index, drops a gbq.cmd shim so the existing gbq query "..." interface still works, registers Task Scheduler jobs (reindex 04:00 + reflection 12:00/23:00), and merges the global CLAUDE.md. Tested: brain_search indexes + queries a real vault on macOS; test-hooks.sh 15/15. install.ps1 still needs a real-Windows smoke test. (BM25 = keyword ranking; embeddings remain an optional upgrade for machines that can pip install sentence-transformers.)

What was macOS-only (the original inventory)

Everything macOS-specific, by layer:

Layer macOS today Windows needs
Installer install.sh (bash); uname = Darwin || die; brew install.ps1 (PowerShell); OS detect; winget
Scheduler launchd plists (nightly 04:00 + reflection 12:00/23:00) Task Scheduler (Register-ScheduledTask)
Safe wrapper gbq (zsh) cross-platform wrapper (Node or Python)
Nightly gbrain-nightly.sh (bash) PowerShell, or rewrite logic in Python
Hooks (Python) hardcoded /tmp/... locks, __HOME__/.local/bin/claude tempfile.gettempdir(), resolve claude on PATH
Obsidian brew install --cask obsidian winget install Obsidian.Obsidian

The gating dependency — gbrain on Windows (researched 2026-06, lifted 2026-10: see the update above)

brain-in-a-box sits on gbrain (separate project, bun-based). The foundation is Windows-ready: bun's Windows support went stable in bun 1.2 (Jan 2026; ARM64 in 1.3.10), and PGLite is WASM so it runs wherever bun/node runs. So the building blocks are fine.

gbrain itself is the blocker. It is not CI-tested on Windows (CI = macOS + Ubuntu only), and its issue tracker has open/known Windows bugs — crucially on the solo/PGLite path we'd target:

  • #1549 — PGLite on Windows 10: the pgvector extension is missing from the WASM binary → search/think don't work. This is the dealbreaker: no semantic search = no brain.
  • #1605 — Supabase-pooler migration getaddrinfo ENOTFOUND on Windows (team path, less relevant to solo).
  • #1554 — cross-platform node shim (PR): the POSIX-shell postinstall didn't run on Windows.
  • #1665 — "critical fix wave" merged Windows migration-spawn fixes.

So fixes are actively landing upstream, but as of this research a solo Windows user cannot get working semantic search until #1549 (PGLite pgvector on Windows) is resolved. Our port is gated on that. Building install.ps1 before gbrain's PGLite works on Windows would ship a broken brain.

Plan

Phase 0 — Spike (gating). On a real Windows box: install bun (≥1.3.10), gbrain init --pglite, embed, gbrain sync, and crucially gbrain query — this is the check for issue #1549 (PGLite pgvector on Windows). If query returns ranked results → green, proceed. If pgvector is still missing → stop; the port is blocked upstream. Track gbrain #1549 / #1554 / #1665. Decision point.

Phase 1 — Cross-platform hooks (also benefits macOS). Replace /tmp with tempfile.gettempdir(); resolve the Claude binary via shutil.which("claude") with the __HOME__ path as fallback; audit for any other POSIX assumptions. Pure Python, low risk. Keep test-hooks.sh green; add a PowerShell sibling.

Phase 2 — Scheduler abstraction. A thin installer step that registers the two jobs with the OS scheduler: launchd on darwin (today), Task Scheduler on Windows (nightly 04:00 + reflection 12:00/23:00, running python daily-reflection.py and the nightly).

Phase 3 — gbq cross-platform. Port the zsh wrapper (force-kill on PGLite read hangs, clean wait on writes, stale-lock sweep) to a small Node or Python script that runs everywhere. Single source of truth, drop the zsh version or keep it as a thin shim.

Phase 4 — install.ps1. PowerShell mirror of install.sh: copy vault skeleton, install hooks (__HOME__ → $HOME replace), install/clone gbrain, Obsidian via winget, register scheduled tasks, merge global CLAUDE.md. Non-destructive, same as the bash installer. Replace the uname guard with OS detection that routes to the right scheduler.

Phase 5 — Tests + CI. A cross-platform test-hooks (PowerShell or a Python runner) and a windows-latest entry in the CI matrix so it doesn't regress.

Phase 6 — Docs. README + CONTRIBUTING: drop "macOS only", add Windows setup.

Scope decisions

  • Solo + PGLite only for v1. Defer team mode and the Supabase engine (that's where the known Windows bug lives).
  • WSL is not the target — native PowerShell, so a non-technical Windows user isn't asked to install a Linux subsystem. (WSL would "work" trivially but isn't a real Windows port.)

Risks

  • gbrain-on-Windows (gating, Phase 0).
  • CRLF line endings breaking the Python/JSON hooks — enforce LF via .gitattributes.
  • Task Scheduler quirks (working dir, env, user session) vs launchd's model.
  • Path separators / %USERPROFILE% vs ~ — handled by pathlib/os.path if we remove the remaining hardcoded POSIX paths (Phase 1).

Recommendation (2026-06, superseded by the 2026-10 update)

Don't build the Windows port yet — it's gated on gbrain fixing PGLite/pgvector on Windows (#1549). The right move now:

  1. Phase 1 (cross-platform hooks) regardless — pure Python cleanup, makes the hooks better on macOS too, and is the only part not blocked by gbrain.
  2. Watch gbrain #1549 (semantic search on Windows PGLite). The instant it's fixed, run the Phase 0 spike to confirm, then do Phases 2–6.
  3. Until then, point Windows users at the standalone git-only journal tool (devjournal) for the part that doesn't need gbrain — they get the journal, just not the searchable brain.