Bring the Nexus/Praxis/Hexis integration in line with MAVEN_ECOSYSTEM_ARCHITECTURE.md: - Praxis over HTTP: drop the in-process praxis.db open (praxisstore/ praxistools) and call praxisd's /api/v1/tools/* API via a new praxisClient. Honors the "no component reads another's DB" invariant (AC#12). PraxisConfig.DBPath -> URL. - Hexis confirmation gate: mutating capabilities (ReadOnly=false) now park a bound pendingHexis confirmation and require a spoken "да" before executing; read-only run immediately (AC#7, no auto attention->action). - Capability safety: >1 verb match is ambiguous -> ask instead of firing the first; ambiguous Nexus resolution asks for clarification (AC#2). - Correlation IDs on Hexis execute, recorded in the cross-service trace. - Bug: importance arrives as JSON float64 over HTTP, not int. - Tests: confirm-gate, decline, read-only, and ambiguity paths. Build: vendor/ bakes in the hexis client (replace-directed at a sibling repo outside the Docker context); Dockerfile builds from vendor and no longer `go mod download`s the unreachable replace paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
modernc.org/libc is a partial reimplementation of C libc in pure Go. It is the runtime for C programs translated to Go by the modernc.org/ccgo transpiler (notably modernc.org/sqlite). It is not a standalone library for general use; its API tracks the needs of ccgo-generated code and may change incompatibly between versions.
Compatibility note: callers of this package (e.g. modernc.org/sqlite) must use the libc version that matches their generated code (see the consumer's go.mod). Do not bump the libc version of a downstream consumer in isolation without re-translating its C sources.
Architectural split: musl-derived vs hand-written
This is the most important thing to understand before editing any file. There are two completely separate code paths selected by build constraint:
-
musl-derived (linux/amd64, arm64, loong64, ppc64le, s390x, riscv64, 386, arm) — the libc implementation is generated by transpiling musl-libc's C source to Go via
ccgo/v4. The canonical build tag is://go:build linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm)Generated files for this path:
ccgo_linux_*.go(~4 MB each, the bulk of the musl translation), the per-archmusl_*.gofiles written by older tooling, and on amd64 the assembly stubs inabi0_linux_amd64.{go,s}(generated byqbecc --abi0wrap). The hand-written glue lives inlibc_musl.go,etc_musl.go,mem_brk_musl.go,mem_musl.go,memgrind_musl.go,pthread_musl.go,syscall_musl.go,aliases.go,atomic.go,atomic{32,64}.go,builtin{32,64}.go,libc_linux_statfs.go. -
Hand-written (darwin, freebsd, netbsd, openbsd, illumos, windows, linux/mips64le) — the libc surface is implemented by hand in Go, with the negated build constraint:
//go:build !(linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm))Main files:
libc.go,libc_unix.go,libc_unix{1,2,3}.go,libc_<goos>.go,libc_<goos>_<goarch>.go,libc_windows*.go,pthread.go,mem.go,memgrind.go,mem_brk.go. These ports also pull in an older set of ccgo/v3-generatedmusl_<goos>_<goarch>.gofiles for the platform-independent pieces (strtod, math, network, etc.).
When you make a change you almost always need to make it in both code paths. Look at the existing build tag of the file you're editing — if it has the long linux && (amd64 || ...) form, you're in the musl path, and there's a sibling file with the negated tag that needs the parallel change.
Common commands
Build / sanity check:
go build ./...— current platform onlymake editor— fast iteration: compile tests to /dev/null, build generator, lint generated asmmake build_all_targets— full cross-compile sweep across all supported GOOS/GOARCH × build tags (slow; required before submitting perCONTRIBUTING.md)make vet— vet, filtering out known abi0 false positives
Tests:
make short-test—go test -shortplus log-grep summarymake test— full test, 24h timeoutmake xtest— full test with-tags=ccgo.dmesg,ccgo.assert(verbose ccgo debugging)make libc-test— runs onlyTestLibc, which transpiles and runs the musl libc-test suite undertestdata/nsz.repo.hu/libc-testmake xlibc—TestLibcwith debug tagsmake membrk-test— full test with-tags=libc.membrk(the sbrk-style debug allocator)- Run one test:
go test -v -run TestStrtod -count=1 - Filter
TestLibcsubtests:go test -v -run TestLibc -re '<regexp>'(the-reflag is defined inall_musl_test.go)
Regenerate musl-derived code:
make generate— downloads the pinned musl tarball if needed, re-translates it viago run generator.gointo$DIR(default/tmp/libc), then rebuilds and short-tests. Only works on linux. Output files (ccgo_linux_*.go, etc.) are committed to the repo and should not be hand-edited.make dev— same asgeneratebut withGO_GENERATE_DEV=1and-tags=ccgo.dmesg,ccgo.assertfor ccgo debugging- Cross-arch generation: set
GO_GENERATE_GOARCH=arm64etc. Thebuilder.jsonautogenregex lists which arches CI auto-regenerates; recent commits likepi32 auto generateare these. - Pinned musl version:
internal/archive/archive.go(currentlymusl-7ada6dde6f9dc6a2836c3d92c2f762d35fd229e0).
Build tags
Functional tags (passed via -tags=):
libc.membrk— replaces malloc with a fixed-size sbrk-style allocator (1 GiB on 64-bit),freebecomes a no-op, heap pre-filled with PRNG bytes. Used to surface use-after-free in downstream code.libc.memgrind— wraps the allocator with an audit table;MemAuditStart/MemAuditReportdetect leaks. Adds overhead.libc.dmesg— appends a per-pid log to/tmp/libc.logof major libc events.libc.strace— flips__ccgo_stracetotrueso everyXfooentry prints a trace line. Heavy.libc.memexpvar— exposes allocator stats throughexpvar.ccgo.dmesg,ccgo.assert— consumed by ccgo during generation, not at runtime.
libc.membrk and libc.memgrind are mutually exclusive (the file build tags enforce it).
Symbol naming conventions
ccgo encodes C identifiers with prefixes; the same scheme is used in hand-written files for consistency:
Xfoo— externally visible C functionfoo(e.g.Xmalloc,Xputchar)Yfoo— abi0-wrapped entry toXfoo(linux/amd64 only, inabi0_linux_amd64.go)Tfoo_t— typedef'd C typefoo_t(e.g.Tsize_t,Tssize_t)Sfoo— struct tagfoo,Ufoo— union tag,Ffield— struct fieldX__foo/X___foo— C identifiers starting with_get folded; both__errno_locationand___errno_locationexist- Lowercase
xfoo,_foo— internal helpers
capi_<goos>_<goarch>.go lists every exported symbol — these are the source-of-truth files ccgo consults when linking against this libc. If you add a new Xfoo, add it to the relevant capi_*.go files too (the generators do this automatically for the musl path).
TLS — thread-local state
A *TLS represents a C "thread". Every Xfoo takes one as the first argument. TLS is not safe for concurrent use — one goroutine per TLS.
- Inside an
Xmain-style entry point:Start(main)creates the main TLS, locks the goroutine to the OS thread, and runsmain. - Library callers (e.g. sqlite): call
NewTLS()per goroutine,defer tls.Close(). The firstNewTLS()is the "main" thread and is not OS-thread-locked. - Pthread support:
Xpthread_createspawns a goroutine with its own TLS. The musl pthread path (pthread_musl.go) and the hand-written path (pthread.go) implement this separately. tls_linux_amd64.{go,s}— assembly stubs that let abi0-translated code reachtls.Alloc/tls.Freewithout going through Go calling conventions.
Subpackages
The repo contains a constellation of subpackages under errno/, fcntl/, pthread/, signal/, stdio/, stdlib/, sys/{types,socket,stat,vfs,random}, time/, unistd/, pwd/, grp/, netdb/, netinet/, etc. They are thin: mostly typedefs and integer constants for use by ccgo-generated callers. They are not meant to be imported by general Go code.
internal/archive — declares the pinned musl version; internal/autogen and internal/overlay — supporting files for the generator.
What is generated vs hand-written
Code generated (do not hand-edit; regenerate via make generate):
ccgo_linux_*.go— the bulk of the musl translation, one per Linux archmusl_<goos>_<goarch>.go— older ccgo/v3 generation for non-Linux ports and some helpersabi0_linux_amd64.{go,s}— qbecc-generated abi0 wrapperscapi_*.go— exported-symbol manifests (regenerated by ccgo)
Anything else (libc.go, libc_musl.go, libc_<goos>.go, libc_unix*.go, mem*.go, pthread*.go, aliases.go, etc.) is hand-written.
generate.go and genheaders.go are old (//go:build ignore) ccgo/v3 helpers, kept around but mostly superseded by generator.go (ccgo/v4).
Debugging artifacts in the tree
Several large files in the repo root are debug logs / surface dumps (log, log-test, log-generate, surface.old, surface.new). They are tracked but stale; do not depend on their contents.