Xo is experimental. The source code is not public yet; it will be soon. Read the release notes

Xo v0.8.0

2026-10-09

Spec draft 0.8.

  • Modules (decision 0086 steps 1 and 2): require <path> <version> of Xo modules, minimal version selection, git fetch into a read only module cache, xo.lock hashes verified on every build, xo get, xo mod tidy, xo vendor, xo mod clean, --offline, and xo.work workspaces (--workspace=off). Diagnostics XO1301 to XO1308.
  • Codex r2 first run failures: XO0175 for several match arms on one line has a split fix and no longer cascades into a false XO0301; .display() works on every Display type (derived, std errors, builtins); a closure that never completes infers Never instead of XO0402.
  • Effect ceilings and checked semver (decision 0086 steps 3 and 4): require <path> <version> uses none in xo.mod and use <dir> uses ... in xo.work (XO1309 at a call above the ceiling), API and effect diffs from xo get (and xo get -u), xo publish [--check] against the previous tag (XO1310, XO1311; prints the tag command, never pushes), xo init, /vN major version module paths, and xo serve noticing a new or changed xo.work above the directories.
  • std/time (decision 0094, Go backend): zones with per use embedded data (--tzdata=system), civil dates and times with explicit daylight saving gaps and overlaps, layouts with named fields checked at compile time (XO0420; XO0177 for {yyyy}, %Y, 2006-01-02 habits).
  • Import direction rules (decision 0095): deny <pkg> -> <pkg>, ... lines in xo.mod with /... patterns, checked at every use line (new code XO1312; a rule naming no package is XO1301), kept by xo mod tidy; xo query imports and xo query deps list the use lines (also in the MCP query tool).
  • std/time error text (std.md 13): ZoneErr and CivilErr display as sentences (unknown time zone "Mars/Olympus", 2026-03-08T02:30:00 America/New_York does not exist (skipped by a daylight saving change)) instead of the derived Skipped(at: ...) form, matching ParseErr and LayoutErr.
  • Newtypes (decision 0097, spec audit gap 4): e.0 reads the underlying value and Email(s) destructures it in let and match.
  • xo check --fix (decision 0096): applies the unambiguous machine fixes (as xo fix), reformats, then reports what remains plus an applied list; the MCP check tool takes fix, and the bench harness agent loop uses it while still scoring first runs as written and after fix.
  • Go backend output (decision 0098): hello world 7.9 to 3.6 MB, Go build cache growth per program 34 to 2.9 MB, compile memory 286 to 123 MB. Binaries carry no DWARF or symbol table (XO_GO_DWARF=1 keeps them for a debugger); programs that do not import std/http do not link net/http.
  • Spec audit second pass: x.debug() works on every value (std.md 1; was XO0601 on concrete types); new tests for single field positional variants, Secret redaction in Debug and log fields, target at run time, Stdio.write, Fs.cwd, Fs.case_sensitive, a real Proc.run, XO-F005, xo test --real, and byte identical rebuilds (spec 12.1 now says “in the same directory”).
  • Modules in repository subdirectories (decision 0086): a module path github.com/acme/tools/cli is the directory cli/ of the repository github.com/acme/tools, versioned by tags cli/vX.Y.Z (as Go); fetch, MVS, xo.lock, xo vendor, xo get, and xo publish [--check] (prefixed tag command) handle it; nested modules are left out of a module’s tree and hash.
  • Go calls in record and replay (decision 0093 stage 1): --record records use go calls whose arguments and results are values (an argument hash and the results) and xo replay answers them without calling Go; calls with a Go handle or a callback run again and the replay reports re executed foreign call `<name>` ; pure std packages and xo.mod replay rerun go <path> [names] lines are not recorded. Binding format v5; traces gain "foreign": 1, and older traces still replay (every Go call runs again).
  • Release builds (decision 0099): xo build --release compiles out requires(debug) and ensures(debug) on every backend and reports XO1308 for workspace overrides; --ci does not imply it.
  • Lazy error frames (decision 0092, Go backend): frames keep copies of the parameter values and format them, bounded to 64 bytes, only when a trace is printed. 1,000,000 errors through 5 frames: 0.93 s to 0.10 s with short parameters, 2.78 s to 0.10 s with 90 byte ones; error output unchanged. xo build --release --error-args=false drops the values.