Nine test files each rolled their own isolation, and every one of them
moved HOME alone. On Windows that left them pointed at the real profile:
the init suite merged hooks into the real ~/.codex/config.toml, the
instructions suite handed the real home to SyncGlobalSkills, which
prunes shipped skills out of ~/.claude/skills, and the platform suite
ran MigrateToUnifiedHome against the developer's real ~/.config/gortex.
The platform and config tests were not merely leaking either — they
assert Home() resolves to <temp>/.gortex, which cannot hold on Windows
while the home lookup ignores HOME. They fail there today.
No behaviour changes on Unix; the local clearXDG and isolateInitTestEnv
helpers give way to the shared ones so a tenth copy is not written next.
The daemon ran with no memory envelope: GOGC defaults, no GOMEMLIMIT, and
the only GC tuning scoped to the cold-index window. One allocation burst
(warmup passes, an hourly janitor tick, a whole-graph analysis) pinned the
process footprint at its peak indefinitely — observed at 16.2 GB on a
16 GiB machine, with the GC then burning 27-77% CPU at idle marking the
bloated heap.
Boot now installs a standing soft limit — GOMEMLIMIT respected verbatim
when set, else GORTEX_DAEMON_MEMLIMIT, else daemon.memory_limit from
config, else hostRAM/4 clamped to [1 GiB, 8 GiB] — and the four burst
boundaries (warmup complete, janitor ticks that did work, cold-index
window close, MCP whole-graph analysis) hand the high-water heap back to
the OS via a forced scavenge, logged with the freed byte count.
GORTEX_DAEMON_MEMRELEASE=0 disables the releases. The cold-index tuning
window captures and restores the standing limit, so the two compose.
Row-shedding maintenance (orphan purges, the duplicate-collapse
migration, resolver cleanups) returns pages to SQLite's freelist, where
future writes reuse them — but only VACUUM returns them to the
filesystem, and nothing ever ran it: a live store pinned 4.4 GB of
freelist inside a 6.8 GB file. Boot now measures the freelist right
after the orphan purge and, when the dead fraction dominates (>50% of
pages AND >1 GiB reclaimable AND the volume has 1.5x the file size
available for VACUUM's transient copy), runs a one-time compaction
before the warmup loop starts writing — logged up front as
minutes-scale work, non-fatal on any failure, and skippable with
GORTEX_SKIP_STORE_COMPACT=1. Measured on a byte-copy of that live
store: 81 s to shrink 6.8 GB to 2.06 GB. A memory-mode daemon skips the
whole path via the capability probe; the disk-headroom check reads
statfs through a platform helper with a Windows counterpart.
Gortex split its per-user state across three roots — config in
~/.config/gortex, cache in ~/.cache/gortex, and a flat ~/.gortex for the
store / models / memories. Collapse them into one tree:
~/.gortex/
├── config.yaml, servers.toml
├── cache/ (daemon socket/pid/log, snapshots, eval/token/… caches)
├── store/ (the on-disk backend + WAL/shm sidecars)
├── models/ (downloaded embedding models)
└── memories/
The XDG variables stay an explicit escape hatch: an absolute
XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME (and the GORTEX_DAEMON_*
overrides) still wins and routes that category to the standard
<base>/gortex location, so XDG-strict setups, sandboxes, and the test
suite are unaffected.
internal/platform/xdg.go is the single resolver: ConfigDir/DataDir
collapse to ~/.gortex, CacheDir/OSCacheDir to ~/.gortex/cache, and new
StoreDir / ModelsDir / MemoriesDir hang the durable sub-trees off
DataDir. The scattered callers route through it; the Legacy* aliases
are gone.
MigrateToUnifiedHome folds an older split layout into the new tree on
first run — best-effort, idempotent, rename-based, and a no-op under an
XDG override. It is wired into the root command's PersistentPreRun so it
lands before any command opens the store or reads config. Models are
treated as durable data (kept out of cache so a wipe doesn't drop large
downloads); the stale daemon socket/pid are left to regenerate.
Add internal/platform/xdg.go exposing the three XDG base categories for
Gortex (config / data / cache), each honouring its XDG_*_HOME variable
when set to an absolute path and falling back to the historical Gortex
default when unset. A relative XDG_*_HOME value is ignored per the XDG
spec.
Route every scattered config/data/cache path computation through the
resolver: the global config path and viper search path, the snapshot
FileStore, the eval index cache, the token-count and wiki-enhance
caches, the savings store, the daemon state directory, the per-machine
server id, the saved-scope store, the global memories store, the daemon
servers file, the hook telemetry / session-state files, the embedding
model cache, and the eval quality/recall caches.
Back-compat: when the relevant XDG variable is unset each subsystem
resolves to exactly the location it used before, so existing installs
are not orphaned. Subsystems that historically rooted their cache at
os.UserCacheDir() (savings, server id, scopes, bench) keep that root as
the unset fallback; subsystems on the legacy ~/.gortex directory keep
that as their unset fallback. Only an explicitly set XDG variable moves
data, and it is now honoured consistently on every platform.
Gortex now builds and runs on windows/amd64.
- internal/platform isolates OS-specific runtime primitives — shutdown
signals, process liveness/termination, and detached-spawn attributes
— behind unix/windows files. The daemon, CLI, and servers call
through it instead of using syscall directly.
- internal/daemon: a Windows fdlimit stub, daemon state relocated to
%LocalAppData%\gortex, and the AF_UNIX socket transport reused as-is
(supported on Windows 10 1803+).
- .goreleaser.yml builds a windows/amd64 zip archive and a Scoop
manifest; Makefile gains build-windows; release.yml signs, hashes,
and scans the zip alongside the other artifacts.
- scripts/install.ps1 is a PowerShell installer; install.sh now points
native-Windows users at it.
- CI builds every package for windows and parse-checks install.ps1.
- internal/thirdparty/renameio vendors github.com/google/renameio with
an added Windows implementation — upstream v1.0.1 is Unix-only and
blocked the build through the github.com/coder/hnsw dependency.
daemon install-service stays unsupported on Windows; users run
gortex daemon start --detach instead.