STATUS (re-audited 2026-08-01 against aether 0.472.0). Most of this file has been overtaken by events. Section A is fully shipped, so the bash trampoline is no longer blocked on upstream; section B's filesystem primitives are all shipped. Two items remain genuinely open upstream — see Priority near the end for the current list and for what is now aeb-side work.
Two cautions for whoever audits this next. Both were learned the hard way during this very audit, which reported two things absent that had shipped:
- Verify by CALLING, not by grepping — and search EVERY module, not the one whose name matches. Two separate misses here: the
fswrappers are exported asfs.<op>overfs_<op>_rawexterns (so "fs_mkdir_p" greps as absent), and the entire path surface lives instd.fs, notstd.path—path_clean,path_rel,path_is_within_base,join_cleanand friends. A pass that readstd/path/module.aealone wrote uppath_normalizeas an open ask whenfs.path_cleanhad shipped. A sibling review caught it.- "std has a function with this name" does not mean aeb should use it. std's
fs.path_join/path_dirname/path_basenamediffer from aeb's on trailing separators, absolute right-hand sides, and Windows drive prefixes — see the audit at the end of this file.
aeb is now a bash trampoline (~600 lines — it also carries the
build-level process-group reap + --timeout watchdog, plus the
--vet/--sandbox/remote-agent dispatch arms) plus a suite of
Aether-language tools under tools/. The bash bit sets AETHER /
AEB_HOME / ROOT, auto-detects the Podman socket, runs the build in its
own process group and reaps survivors, and dispatches to tools/aeb-main,
tools/aeb-init, or tools/gcheckout. Everything else — argument
parsing, DAG discovery, dep extraction, topo sort, per-file compile, link,
node scheduling, exec — is Aether.
What's left preventing the trampoline from going away entirely, and what
would let aeb run unmodified on Windows.
The pure-Aether entrypoint already exists.
tools/aeb-cliis a faithful facsimile of the bashaeb— full flag parity (all build flags +--sandbox,--version,--init), the supervised exec (own process group, signal forwarding, timeout, group-reap — one cross-platformos.run_supervisedcall, POSIX process groups / Windows Job Objects), and an A/B harness (ab.sh) proving it produces identical exit code + artifact set + telemetry to the bash trampoline across C/Rust targets. It compiles and serves natively on Windows too. It may become the defaultaebonce the items in §A below are closed upstream in Aether — at which point the bash trampoline is retired andaebis pure Aether end to end (and Windows-native without MSYS/bash). Until then, bashaebships as the default andaeb-cliis the proven-equivalent alternate. Seemingw_a_b_plan.mdfor the A/B methodology.
✅ ALL THREE SHIPPED — this section is no longer blocked on Aether. Re-audited 2026-08-01 against 0.472.0 and functionally tested, not just grepped. What remains is aeb-side work, not an upstream ask.
-
✅ Resolve the running binary's directory.
aether_argv0()is declared instd/osandfs.realpath(p)instd/fs(returns a 3-tuple: path, rc, err). Both verified working.extern aether_argv0() -> string // path the OS launched us with fs.realpath(p) -> (string, int, string) // canonicalize symlinks etc. -
✅ Replace the current process.
os_execvis exported fromstd/os. -
✅ Read environment variables —
os_getenv, solved long ago. Listed for completeness.
So the bash trampoline can collapse into a self-bootstrapping aeb.ae
whenever someone does the work. That is now an aeb task, and
tools/aeb-cli is already most of it (see the note at the top of this
file). Nothing here is waiting on upstream.
The tools all live as separate native binaries that the bash
trampoline (or each other) exec into. Inside each tool, several os_system
/ os_exec calls remain that could become native:
✅ EVERY FILESYSTEM PRIMITIVE ASKED FOR HERE HAS SHIPPED. Re-audited 2026-08-01 against 0.472.0. The remaining work is aeb-side: migrating the call sites off
os.systemstring-building. Nothing upstream.Note the earlier audit nearly got this wrong — the wrappers exist under
fs.<op>withfs_<op>_rawexterns beneath, so a grep for the exact names in this table ("fs_mkdir_p") reports absent. They were verified by CALLING them, not by name.
| Pattern | Used in | Status |
|---|---|---|
mkdir -p |
aeb-init, aeb-link, aeb-main | ✅ fs.mkdir_p(path) |
ln -s / rm / readlink |
aeb-init | ✅ fs.symlink, fs.readlink, fs.unlink |
dirname $(command -v ...) |
aeb-link, aeb-main | ✅ os.which(name) |
test -L / [[ -L ]] |
aeb-init | ✅ fs_is_symlink(p) (raw extern; no wrapper yet) |
git sparse-checkout add ... |
gcheckout | unavoidable shell-out, but see (D) |
gcc -O2 ... (the link step) |
aeb-link | unavoidable shell-out (gcc/clang) |
find ... (already gone) |
scan-ae-files uses fs_glob | done |
sed/grep/awk (gone) |
encode-name, extract-deps, topo-sort, etc. | done |
All verified by calling them on 0.472.0: mkdir_p created a nested tree,
symlink/readlink round-tripped, realpath resolved a/b/../b/c,
unlink removed the link, os.which("sh") returned /usr/bin/sh.
aeb works on Linux and macOS today. For Windows (MinGW / MSYS2 / native)
the gaps are:
-
Symlink semantics —
lib/aether/.aeb/lib/<sdk>is a symlink farm. Windows symlinks need elevation or developer mode. Alternatives Aether could offer:- Junction-point stdlib helper (
fs_junction) for directories - Or: an "embed mode" where
aeb --initcopies the SDK files instead of symlinking, with a watcher to mirror updates - The current
aeb-initalways usesln -s; would need a runtime branch.
- Junction-point stdlib helper (
-
Path separator handling — every tool uses
/literally instring_concatcalls. PARTIALLY SHIPPED as of 0.472.0:fs.path_join(a, b) ✅ present fs.path_is_absolute(p) ✅ present fs.path_clean(p) ✅ present — LEXICAL normalize (a/b/../c -> a/c), no filesystem access, so it works on paths that do not exist yet. `fs.clean` is the wrapper; `fs.join_clean(a, b)` joins then cleans. Also `fs.path_rel`, `fs.path_is_within_base`. path_separator() ❌ absent — but not worth asking for: path_join emits "/" (accepted by nearly every Windows API) and `os.platform()` already lets a caller branch.NOTE THE MODULE. All of this lives in
std.fs, NOTstd.path. An earlier pass of this audit checkedstd/path/module.ae, found nopath_normalize, and wrote it up as an open upstream ask. A sibling review caught it. Verified since by calling:/nope/x/../y->/nope/yon a nonexistent path.Note aeb does NOT simply want
fs.path_joinhere — its own_path_joinhonours an absolute right-hand side and Windows drive prefixes, where std's returnsa//b. See the audit at the end of this file. Nothing in C2 is an upstream ask any more; adoptingfs.cleanat aeb's ~16 hand-rolled concat sites is aeb-side work. -
Process launching — ✅ SHIPPED. The argv-based launch this asked for landed in aether 0.124 as
os.run_capture(prog, argv, env)andos.run_pipe*(incl. the deadlock-freeos.run_pipe_drain_and_wait), whichaebnow uses (e.g.aether.driver_test). It does what this item wanted — no shell in the middle, eliminating a class of quoting bugs on every platform. The original ask, for the record:os_systemon Windows runs throughcmd.exein some runtimes, and the shell strings (>/dev/null,&&,2>&1) don't all work there; an argv-basedos_run(prog, argv, env)/os_run_capture(...)that doesn't use a shell fixes it.Remaining: not every aeb shell-out has been migrated off
os.systemstring-building toos.run_captureyet — that's an aeb-side cleanup, not an upstream ask. -
fs_globrecursive walker — already POSIX-only on the C side (usesdirent/fnmatch). Windows needs theFindFirstFile/FindNextFilepath implemented or a port of fnmatch. This sits in the Aether runtime, not inaeb. -
Podman/Docker socket detection — only relevant when a build's tests use TestContainers. The trampoline currently does
[[ -S "/run/user/$(id -u)/podman/podman.sock" ]]and exportsDOCKER_HOST. STILL BLOCKED (re-checked 0.472.0): neither exists.extern fs_is_socket(path: string) -> int ❌ absent extern os_user_id() -> int ❌ absentfs.try_stat+fs_get_stat_kindmay already answer the socket half; worth checking before filing. -
gccinvocation — the link step assumes a POSIX shell line. On Windows we'd point atgcc.exe(MinGW) orcl.exe(MSVC). The argv layout is similar; the wrappers and-L/-lflags differ for MSVC. Out of scope foraebitself but worth noting that Aether's own compiler assumes gcc-compatible flags.
Moved to TODO.md on 2026-08-01, under the --since VCS item it
belongs beside.
This section asked for a runtime extern vcs_sparse_add(repo, path)
implemented per VCS in the Aether stdlib, so aeb gcheckout could narrow
a working copy under Mercurial/Subversion/jj as well as Git.
That is the wrong layer. Aether is a language runtime and has no
business knowing about VCS technologies — it would be a large, drifting
surface (four tools × their flags × their versions) added for exactly one
consumer, and every other Aether program would carry it. The alternative
the section itself offered — a per-repo-kind command table on aeb's side,
with a .aebvcs escape hatch — needs nothing from upstream at all.
Kept as a stub rather than deleted so the section letters in this file stay stable and anyone following an old reference lands here.
These were in the previous iteration of this doc and have since landed:
- ✅ String memory leak in loops fixed (commit on
feature/tinyweb-aeocha, also in PRfix/heap-tracker-and-fs-glob-dotfiles). - ✅
fs_globrecursive walker matches dot-prefixed files (same PR). - ✅
_heap_<var>lazy declaration in codegen (same PR). - ✅ Pure-Aether string operations are now sufficient: we have
string_index_of,string_substring,string_concat,string_trim,string_ends_with,string_starts_with,string_length, plus a userlandstring_replace_allintools/encode-name.ae(~12 lines). (Superseded 2026-08-01: aether 0.463.0 added a nativestring.replace_all, whose C symbol collided with that userland copy and broke the link. aeb now uses the native one and keeps no copy —AETHER_PINis 0.463.0 for exactly this. Seeitests/std-symbol-collision.sh.) - ✅
aebmigration itself — encode-name, infer-type, file-to-label, resolve-dep, extract-deps, scan-ae-files, topo-sort, aeb-link, aeb-init, aeb-main, gcheckout all in Aether.
Re-audited 2026-08-01 against 0.472.0. Items 1, 3 and 4 of the previous list have all shipped — so most of what this file asked for is done, and the bulk of the remaining work is aeb-side rather than upstream.
Still open UPSTREAM, in rough value order:
fs_globWindows backend — the POSIXdirent/fnmatchwalker. Still the highest-value open item: the only thing intools/that absolutely will not run on native Windows.- A socket test +
os_user_id— the Podman-socket probe (C5). CHECKED:fs.try_stat/fs_get_stat_kinddoes NOT cover it. Its kind encoding is1=file, 2=dir, 3=symlink, 4=other, and a real AF_UNIX socket and a real FIFO both return 4 — verified. So either extend that encoding (5=socket, …) or addfs.is_socket.os_user_idhas no equivalent under any spelling. Now aeb-SIDE work, not upstream asks:
- Collapse the bash trampoline into
aeb.ae.aether_argv0,fs.realpathandos_execvall exist;tools/aeb-cliis already most of the implementation. - Migrate
aeb-initoffln/readlink/test -Lontofs.symlink/fs.readlink/fs.unlink/fs_is_symlink. - Migrate remaining
os.systemstring-builders ontoos.run_capture. - Adopt
fs.clean/fs.join_cleanat the ~16 sites intools/that hand-rollstring.concat(..., "/"). Lexical, so it is safe on paths that do not exist yet. Does NOT replace aeb's own_path_join/_dirname/_basename— their divergence from std is deliberate and measured (see the audit below).
NOTE ON FILING. None of the open items above have ever been filed as
aether issues — this file is where they have lived. The repo convention
for an upstream ask is asks/aether-*.md carrying an **Upstream issue:** link (see asks/aether-actor-synchronous-call.md). The open
items should move to that shape; this file should keep only aeb's own
status and history.
Prompted by string_replace_all — where a local helper duplicated
something std had gained. Swept every top-level function aeb defines
against std's full exported/extern symbol set (~1355 names, read from the
installed toolchain) to find others.
Method note: matching by name alone produced ~46 hits, nearly all
noise — SDK setters (release, path, bin, arg) that merely share a
spelling with a std function and compile to <module>_<name>, so they
never collide or duplicate. The signal is in the underscore-prefixed
private helpers: those exist because someone needed a utility, which is
exactly where std may now have one.
| aeb helper | std equivalent | Verdict |
|---|---|---|
_index_of_from (tools/aebcli) |
string.index_of_from |
REMOVED — identical over 7 cases |
_dirname / _basename / _path_join (lib/build) |
path.* |
KEEP — semantics differ, see below |
_dirname (aeb-query, affected-targets) |
path.dirname |
KEEP — differs from std AND from lib/build's |
_to_int (lib/agent) |
string.to_int |
KEEP — deliberately lenient |
_csv_split (lib/agent) |
none | not a duplicate (splits + trims + drops empties) |
_abs (aeb-trace) |
none | shells out to pwd |
Why the path helpers stay. std's are not a drop-in replacement —
measured on 0.463.0. (They live in std.fs as path_join/path_dirname/
path_basename, not in std.path; an earlier pass of this audit looked
in the wrong module and drew a wrong conclusion from it.)
| input | aeb (lib/build) | std (std.fs) |
|---|---|---|
dirname("foo/bar/") |
foo |
foo/bar |
basename("foo/bar/") |
bar |
"" |
join("a", "/b") |
/b |
a//b |
aeb's versions ignore trailing separators, honour an absolute right-hand
side, and understand Windows drive prefixes (C:\foo → C:). These
derive build labels and target directories — the addressing contract — so
a silent semantic change there would misroute artifacts, not just look
untidy. Swapping them needs std to match, or a deliberate migration with
the label tests as the gate.
Worth noting: aeb-query's _dirname returns "" for a bare name
where lib/build's returns ".". That divergence predates this audit and
is a latent inconsistency, not something std would fix.
_to_int is a different contract, not a worse one. std's fails closed
("12abc" → error, "" → error); aeb's is lenient by design ("12abc" →
12, "" → 0) for parsing agent fields where a partial value is wanted.