Status (2026-07-30, evening): asks 1–5 below are DONE and shipped in koine 0.1.0. cljgo closed them with Clojure-shaped APIs —
cljg.system/getenv,cljg.process/spawn+cljg.stream,cljg.net.http:as :stream,cljg.date/nano-time— so no koine branch usesrequire-goat all, and the nil-substituting AOT discovery-pass constraint below never applies to koine. All of it is conformance-tested on both supported hosts, interpreted and fromcljgo buildbinaries, including a real MCP stdio handshake.The next round of asks lives in cljgo ADR 0110 — byte-level I/O, ISO-8601 dates, base64, and three
clojure.coreparity bugs (strof a UUID emits#uuid "…";clojure.string/replacerejects a function replacement). Two previously-carried divergences were retracted there on measurement: the numeric tower andformatboth match the JVM. This file is kept as the record of round one; 0110 is the live list.
Round one (historical): the four blockers are CLOSED upstream. Carried as cljgo ADR 0104 — "require-go reaches the Go standard library" (renumbered from 0096, which collided with the contrib-tier1 ADR). Decision 1 is now implemented and proven in cljgo commit
4423d12: spikes S56–S58 build real AOT binaries that read an env var (os.Getenv), drive a long-lived child over piped stdin/stdout (exec.Cmd.StdinPipe— this is MCP stdio transport), stream an HTTP response body incrementally (resp.Body, notio.ReadAll), and read a monotonic clock (time.Since).Per the ADR's own sequencing, this lands with no koine change — the seam absorbs it. koine's cljgo branches can stop throwing named errors for issues 1–4 once a cljgo carrying
4423d12is released. Clojars consumption is separately covered by cljgo ADR 0095.Three issues below were corrected on re-measurement (S56): #7 is retracted (the claim was backwards —
clojure.coreprivates are reachable fully-qualified), and #4 and #10 drop to minor (clojure.core/-nano-timeresolves when qualified;mkdirs,move!,stat,size,temp-diralready exist incljg.io, so crash-safe write via rename is possible today). This file remains the koine-side statement of need; ADR 0104 is the design, the evidence and the effort estimate.One new constraint for koine, from S57/S58. cljgo's AOT discovery pass evaluates your Clojure with
nilsubstituted for every host result, so a nil-intolerant pure function applied to a host value breaks the build, not just the run —(clojure.string/trim (.ReadString! rdr 10))fails at compile time with "trim expects a string, got: nil". koine's cljgo wrappers must keep host results on a nil-tolerant path. Also:lang.Chardoes not coerce to Gobyte— pass the integer (10, not(char 10)).
cljgo is a tier-1 host: a gap here blocks a koine release. Everything below was measured on 2026-07-27 against cljgo 0.1.0-dev (both the installed binary and the in-repo one), and each item says exactly what unblocks.
Ranked. Ask 1 alone closes items 2–5, so it is worth doing first even though it is the largest.
require-go should reach the Go standard libraryToday: the seed registry is five packages — strings, strconv, math,
fmt, net/url (pkg/corelib/host.go:33-80). os, os/exec, time and
net/http are all absent.
(require-go '[os]) ;=> error: no such namespace: os
(require-go '[net/http]) ;=> returns cleanly, interns NOTHING;
; the later http/Get fails "no such namespace: http"
Resolved by spike (2026-07-27): stdlib fails in AOT too, but not because
the machinery is missing. A real third-party module builds and runs end-to-end
with zero hand-written bindings (github.com/google/uuid pinned via
go-require → a 6.8 MB binary printing a real UUID). Stdlib is excluded purely
by a gate: isThirdPartyGoPath (pkg/eval/host.go:174) requires a dot in the
first path segment, and os has none. So the AOT half is a 3-call-site
change in one file, not new machinery. See cljgo ADR 0096 decision 1.
Why it matters: Go already has everything koine is missing —
os.Getenv, time.Since, time.Sleep, exec.Cmd.StdinPipe, resp.Body. One
registry change closes items 2, 3, 4 and 5 below at once, and makes cljgo a
first-class Go host rather than a special case. It also future-proofs: koine's
Go branch could then be written against the Go stdlib and work on any Go-hosted
Clojure, present or future.
Note: require-go returning cleanly while interning nothing is a bug in its
own right — a clean return reads as a successful capability probe.
cljg.os/getenvToday: cljgo has no way to read an environment variable at all. Verified:
cljg.os is cron/service only, System/getenv does not exist, (getenv …)
does not resolve, and require-go '[os] fails.
(cljg.os/getenv "HOME") ; -> string, or nil when unset
Unblocks: koine.env, which currently throws a named error on cljgo. That
in turn blocks toolnexus's ${ENV_VAR} expansion in MCP headers — i.e. all
remote-MCP authentication.
Note the semantics: Go's os.Getenv returns "" for unset where the JVM
returns null, and "" is truthy in Clojure. Please return nil, not
"", or every (or (getenv x) default) in the ecosystem silently misbehaves.
cljg.io/spawnToday: cljg.io/exec (core/cljg/io.cljg:155) is run-to-completion — it
takes :in as a string and returns after the child exits, so it cannot hold a
conversation.
(let [ch (cljg.io/spawn ["mcp-server" "--stdio"] {:dir d :env e})]
(cljg.io/send-line! ch json-rpc-request)
(cljg.io/read-line! ch) ; blocks for one line; nil at EOF
(cljg.io/alive? ch)
(cljg.io/close! ch)) ; closes stdin, waits, returns exit code
Go side is exec.Cmd + StdinPipe/StdoutPipe + bufio — the same shape as
pkg/bri/io_proc.go already uses for -proc-exec.
Unblocks: MCP stdio transport — roughly half of toolnexus §2, and the one thing ADR 0009 calls "the only hard blocker."
Ideally also: kill the whole process group on close, so a runaway child tree dies with its parent.
Both already exist in Go and work; only visibility is missing.
-nano-time — a real time.Since(bootInstant)
(pkg/corelib/macro_support_builtins.go:6), defPrivate into clojure.core,
unresolvable from user code.-sleep-ms — same.(cljg.os/mono-nanos) ; monotonic, for durations
(cljg.os/sleep ms) ; block, return nil
Unblocks: koine.time/mono-ms currently falls back to a high-water-clamped
wall clock on cljgo, so durations are wrong across an NTP step; and sleep!
routes through core.async/timeout, which works but is a heavy way to sleep.
Quirk noticed: private vars in the cljg.os namespace ARE reachable when
fully qualified (cljg.os/-sleep-millis resolves), while private
clojure.core ones are not. Inconsistent — worth a look independently.
cljg.net.http/request-streamToday: the only Go shim ends in io.ReadAll(resp.Body) followed by
defer resp.Body.Close() (pkg/bri/net_http.go), so the body is fully
buffered and the reader is closed before Clojure ever sees it. The namespace
exposes no other entry point.
Anything that yields the body incrementally is enough — a reader handle, or a callback per line:
(cljg.net.http/request-stream opts (fn [line] …))
Unblocks: koine.stream/sse-post, which works on JVM, Glojure and let-go
and throws a named error on cljgo. Without it, cljgo cannot consume streaming
LLM responses (toolnexus §8 streaming).
core.async aliases are referred into the user ns only
(pkg/corelib/chan_builtins.go:632, applied by InitUserNS). So <!!,
timeout, chan, go resolve in a script but fail at compile time
inside any (ns …) file. Library code must fully qualify them. Surprising,
and it makes a working script a misleading capability probe.(str e) on an exception prints #object[*lang.ExceptionInfo] — a test
asserting on error text silently passes. ex-message works; consider making
str useful.System/exit equivalent found. Every CLI needs one.cljg.io has no mkdir-p, rename, stat (mtime/size), temp-dir, or
binary read-bytes/write-bytes. rename in particular is what makes a
crash-safe write possible (write temp, then rename); toolnexus's durable task
store needs it.Deliberately not requesting: TLS configuration, DNS, compression, mmap, FFI, SQL drivers. koine's survey across toolnexus, agentic-nexus and rag-cite-nexus found no first-party need for them, and folding them in would make koine a runtime rather than a seam.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |