diff --git a/README.md b/README.md index 11d195f68..940c49acc 100644 --- a/README.md +++ b/README.md @@ -115,12 +115,32 @@ scripts/ 42 checks and the developer loop, in nushell `buck-src/` and `buck-rust/` hold about 260,000 and 3,000 files respectively and are almost entirely gitignored; only their generated `BUCK` files are committed. +### Building without Nix, which is how you should iterate + +**Nix is the packaging, not the build.** The build is Buck2, and you can drive it directly. A +Nix-built prefix takes an hour; a Buck2 rebuild after editing one file takes seconds, and this is +the loop to use while working. + +``` +nix develop # the toolchain only: clang, rustc, buck2, nushell +scripts/buck-setup.nu # writes .buckconfig.local with the store paths of those tools +buck2 build //... # or a single target, which is the point +``` + +`scripts/buck-setup.nu` is what connects the two: it resolves the compiler, the guest toolchain, +the host library directories that `wrapgen` dlopens, and the guest Rust toolchain, and writes +them into a machine-local `.buckconfig.local` that is gitignored. Rerun it after a nixpkgs bump +moves any of those store paths. + +You still need Nix to GET the toolchain, and to produce an installable package. You do not need +it to compile, link, or run the checks. + Run the checks with: ``` scripts/buck-test.nu ``` -[PLAN.md](PLAN.md) is the working record: what is being done, what was measured, and what was +[docs/changelog.md](docs/changelog.md) is the working record: what is being done, what was measured, and what was tried and rejected. [docs/release-readiness.md](docs/release-readiness.md) is what stands between this and a first release. diff --git a/darwin/PlistBuddy/BUCK b/darwin/PlistBuddy/BUCK index db80946ae..d26ff434f 100644 --- a/darwin/PlistBuddy/BUCK +++ b/darwin/PlistBuddy/BUCK @@ -68,7 +68,7 @@ export_file( # PlistBuddy_c, because the parity gate needs both binaries to exist. # # It is 49 CoreFoundation externs plus a port of the 83 percent of the program that never touches -# CF. See PLAN.md #102b for the measurement that decided it was worth doing. +# CF. See docs/changelog.md #102b for the measurement that decided it was worth doing. darwin_rust_staticlib( name = "PlistBuddy_rs_lib", crate_root = "PlistBuddy.rs", diff --git a/darwin/PlistBuddy/PlistBuddy.rs b/darwin/PlistBuddy/PlistBuddy.rs index 3d20c3124..5659a1cfa 100644 --- a/darwin/PlistBuddy/PlistBuddy.rs +++ b/darwin/PlistBuddy/PlistBuddy.rs @@ -20,7 +20,7 @@ along with Darling. If not, see . //! PlistBuddy, ported from PlistBuddy.c (#102). //! -//! WHY THIS IS A PORT WORTH MAKING, from the measurement in PLAN.md rather than from taste: only +//! WHY THIS IS A PORT WORTH MAKING, from the measurement in docs/changelog.md rather than from taste: only //! 17 percent of the C touches CoreFoundation. The other 83 percent is string parsing, a command //! loop, type inference and a hand-rolled pretty printer, over user input, with 64 lines of //! malloc, free, strdup, strndup and alloca. That is the part this rewrite makes safe. The CF diff --git a/darwin/dirserv/dscl b/darwin/dirserv/dscl index 7da5b17fe..e7f1fe4a0 100644 --- a/darwin/dirserv/dscl +++ b/darwin/dirserv/dscl @@ -24,7 +24,7 @@ # This stub operates on the Darling prefix's /etc/passwd and /etc/group # files, NOT the host's. # -# See: PLAN.md (Task 5.1) +# See: docs/changelog.md (Task 5.1) set -eu diff --git a/darwin/dirserv/dseditgroup b/darwin/dirserv/dseditgroup index 1aafe9deb..fc1f61550 100644 --- a/darwin/dirserv/dseditgroup +++ b/darwin/dirserv/dseditgroup @@ -12,7 +12,7 @@ # # This stub operates on the Darling prefix's /etc/group file, NOT the host's. # -# See: PLAN.md (Task 5.1) +# See: docs/changelog.md (Task 5.1) set -eu diff --git a/darwin/dirserv/sysadminctl b/darwin/dirserv/sysadminctl index d0076aea9..e2c25bb6b 100644 --- a/darwin/dirserv/sysadminctl +++ b/darwin/dirserv/sysadminctl @@ -10,7 +10,7 @@ # # This stub operates on the Darling prefix's /etc/passwd file, NOT the host's. # -# See: PLAN.md (Task 5.1) +# See: docs/changelog.md (Task 5.1) set -eu diff --git a/darwin/frameworks/Network/src/nw_stubs.c b/darwin/frameworks/Network/src/nw_stubs.c index 1e0596d30..3ec5516fe 100644 --- a/darwin/frameworks/Network/src/nw_stubs.c +++ b/darwin/frameworks/Network/src/nw_stubs.c @@ -7,7 +7,7 @@ // logs once and returns NULL/0 (which the caller treats as failure). // // Generated from the undefined nw_ symbols of libaws-c-io; see -// PLAN.md. NOT a real Network implementation. +// docs/changelog.md. NOT a real Network implementation. #include #include diff --git a/darwin/loader/Cargo.toml b/darwin/loader/Cargo.toml index c57351ca0..9d34de94b 100644 --- a/darwin/loader/Cargo.toml +++ b/darwin/loader/Cargo.toml @@ -4,7 +4,7 @@ # the commpage, checks in with ciderd, and jumps into dyld's entry point. # # Uses goblin for the Mach-O parse (the mapping/stack/jump are raw libc + asm). See the -# milestones M0-M6 in PLAN.md. +# milestones M0-M6 in docs/changelog.md. [package] name = "loader" version = "0.0.0" diff --git a/darwin/loader/src/main.rs b/darwin/loader/src/main.rs index ebb7d2385..2d5b506c6 100644 --- a/darwin/loader/src/main.rs +++ b/darwin/loader/src/main.rs @@ -3,7 +3,7 @@ // M0/M1 (this file): the crate scaffold, argv-shape detection, special-env handling, and // the Mach-O parse via goblin. The address-space setup (M2), dyld + start stack + commpage // (M3), ciderd checkin (M4), and register setup + jump-to-entry (M5) are the next -// milestones (PLAN.md). All the unsafe mapping/jump work is deferred so +// milestones (docs/changelog.md). All the unsafe mapping/jump work is deferred so // this compiles first and the parse path can be validated on real guest binaries. #![allow(dead_code)] diff --git a/darwin/sandbox-exec/sandbox-exec.c b/darwin/sandbox-exec/sandbox-exec.c index 68bdc97f0..c1e3a218c 100644 --- a/darwin/sandbox-exec/sandbox-exec.c +++ b/darwin/sandbox-exec/sandbox-exec.c @@ -14,7 +14,7 @@ * [-n ] [-D =]... * [args...] * - * See: PLAN.md (Task 2.1) + * See: docs/changelog.md (Task 2.1) */ #include diff --git a/PLAN.md b/docs/changelog.md similarity index 96% rename from PLAN.md rename to docs/changelog.md index 4c21cb822..4d44b239b 100644 --- a/PLAN.md +++ b/docs/changelog.md @@ -1,61 +1,18 @@ -# cider-nix +# Cider development log -> **Cider Isn't Darwin Emulation, Really.** +**What this is.** The record of how Cider got here: the per-task entries below say what was +measured, what was decided and what was rejected, newest first. It is a working document, not a +narrative, and it exists so nobody re-derives an answer that was already paid for. -cider-nix is a Nix-packaged fork of [Darling](https://github.com/darlinghq/darling) -(a userspace macOS/Darwin compatibility layer for Linux, "Wine for macOS"). Its host and -guest runtime have been rewritten in Rust. +**What this is NOT.** It is not the release changelog. That is [../CHANGELOG.md](../CHANGELOG.md), +which is short, user-facing and organised by release. This file is for people working ON Cider. -**End goal:** build `aarch64-darwin` nixpkgs derivations on non-Apple ARM Linux, using -Darling as the Darwin layer, verified bit-for-bit against cache.nixos.org. - -**Current campaign:** make `x86_64-darwin` builds work end-to-end against **nixpkgs 26.05** -(the last release supporting x86_64-darwin: a frozen target and a permanent cache oracle). -x86_64 is the native-speed test rig; most work (libSystem surface, harness, oracle, daemon) -is architecture-independent and transfers to ARM. - -Tag work: **[ARCH-FREE]** (transfers as-is), **[ARCH-PARAM]** (transfers if parameterized -now), **[X86-ONLY]** (throwaway, minimize investment). - -> This file carries what is currently TRUE. `plan/buck2-port.md` carries what does not -> belong here: why the port exists and what done means, the constraints learned from the -> nix-ninja grind, the phase plan and the non-goals. Neither keeps a status log -- the -> history is the commit that made each change. +**Two halves.** The rules and traps come first, because they apply to every task and are the part +worth reading before touching anything. The task history follows, newest first. --- - -## Buck2 port: how to work in it - -The per-task narrative of how the port got here is in `docs/plan-history.md`, moved out -verbatim on 2026-08-11. What follows is only what changes what you do next. - -**CLIMB THE LADDER FROM THE BOTTOM. Never start at the endpoint.** - -1. `scripts/buck-lowering-stage-check.nu` — reads the generated staging script rather than - building anything. Seconds warm, about six minutes cold after a `buck2 killall`. It is the - only thing that can catch a staging fault at all: a build cannot, because the fault is in - the script that lays out the tree the build then reads. -2. `nix build .#cider-buck2-one` — one real target end to end, about 18 s warm. -3. `nix build .#cider-buck2-prefix-min` — the gate, roughly an hour. Launch it only from a - CLEAN working copy: jj auto-snapshots, so uncommitted edits are what Nix builds. - -Three hours went once on chasing a shell-script fault at rung 3. Rung 1 names that class in -minutes, and did exactly that for #87 stage 2. - -**RUN THE NIX-FREE SET FIRST. Nine checks, 27 seconds together, and several have each saved -an hour.** `buck-escape-roots-check.nu`, `buck-pin-patches-check.nu`, `buck-pin-rev-check.nu`, -`buck-env-names-check.nu`, `buck-first-party-paths-check.nu`, `buck-labels-check.nu`, -`buck-pin-paths-check.nu`, `buck-host-includes.nu`, `buck-coverage.nu`. - -**COUNT `^building`, NEVER the "these N derivations will be built" list.** The list overstates -the real work by nearly five times: gate12 listed 4,336 derivations and 895 builders actually -ran, because CA early cutoff removes the rest DURING the build. Judge by builders that ran. - -**A BUCK EDIT IS NOT A FULL REBUILD ANY MORE** (#54, #79). The old advice to batch BUCK edits -is stale; the cascade was cut from 1,558 builders to 44. - -**A RUNG 3 BUILD CAN WEDGE, SO WATCH IT:** `scripts/buck-stall-watch.nu `. +## Rules, traps and standing decisions ### cmake is gone (#82), and five generators are FROZEN @@ -70,12 +27,14 @@ offers to freeze its threshold, suspect the POPULATION first. `result-graph-stock` beside it is ALREADY collected, so this is not hypothetical: a check whose input can vanish must fail rather than pass when it does. + ### The rename rule (#84, #78) **Upstream keeps the old name in its paths, patch headers, repo and org; only FIRST-PARTY names change.** `__DARLING__` is upstream, 772 times across 188 pin files, and keeps its name. `buck-upstream-names-check` holds the upstream names as DATA and must never be swept. + ### Moving a path root needs THREE audits, each blind to the next (#87 stage 2) 1. **File content.** A sweep sees it. Be LABEL-FIRST: `buck-src/` alone mentions the old root @@ -137,6 +96,7 @@ the last two are the point: move the dylib off the rpath and the run must fail N `@rpath/libciderrpath.dylib`, then set `DYLD_LIBRARY_PATH` and the same layout must pass again, which is what separates an rpath-expansion failure from an unloadable dylib. + ### What 100 percent does NOT mean - **32-bit is not built and will not be.** `libsyscall_32` and the 74 i386 mig edges. A @@ -150,6 +110,7 @@ which is what separates an rpath-expansion failure from an unloadable dylib. 292 of 336 installed dylibs and framework binaries load in the guest, and the 44 that do not are the Swift LFS pointers (#39), which are not libraries. Past dlopen is unmeasured. + ### Deliberate divergences from the reference Three, each stated in the code beside the reasoning rather than done quietly: @@ -162,6 +123,7 @@ Three, each stated in the code beside the reasoning rather than done quietly: - **perl 5.18 Storable** is compiled with `-DVERSION=2.41` to match its own `.pm`, over the reference's `2.4`. + ### Traps that have each cost at least one increment **A name does not identify an artifact.** This was wrong in three separate places and cost @@ -215,6 +177,7 @@ rewritten to a Nix BINDING name, which matches no directory, and all 1798 lowere failed with "Permission denied" 90 minutes into a build. `scripts/buck-lowering-stage-check.nu` reads the generated staging script in seconds instead. + ### The meta-lesson: re-test a recorded diagnosis before acting on it Recorded diagnoses in this file keep turning out to be untested guesses, and most point away @@ -242,6 +205,7 @@ Corollaries that have each been paid for separately: - **Evidence that looks conclusive often is not.** libpthread bzeros `main_stack` on BOTH its success and failure paths, so a blanked value proves only that the function ran. + ### Where the narratives went Each fix above had a long writeup here. They are all in the commit that made the change, @@ -351,100 +315,6 @@ referenced is completed. Git history holds them. What follows is only what is still OPEN. -### #95 - migcom stamped the build time into every stub, so 110 groups never cached (DONE) - -migcom wrote the wall clock into every generated stub, so two builds of the same inputs at -different times produced different bytes, and under content addressing that defeated early -cutoff for everything downstream of a mig group. Found by #66's full-graph comparison: 1,364 of -1,474 groups identical, 110 differing, all 110 mig. - -Fixed by `patches/bootstrap_cmds/0001-migcom-honour-source-date-epoch.patch`. A freshly built -mig group now reads `stub generated Tue Jan 1 00:00:00 1980`. Guarded by -`scripts/buck-mig-epoch-check.nu`, which builds ONE mig group instead of the whole graph and is -in `buck-test.nu`. - -Full detail, including the copy I got wrong first: docs/plan-history.md, "#95 in detail". - -### #96 - a first-party source edit and the group cascade (ANSWERED 2026-08-12: THERE IS NO CASCADE) - -**THE PREMISE IN THE OLD TITLE WAS FALSE.** It read "a first-party source edit still -invalidates every group that stages the project". Measured on the endpoint, it invalidates -**zero** groups. - -**THE MEASUREMENT.** `scripts/buck-quick-check.nu --attr .#cider-buck2-prefix-min --probe -darwin/frameworks/AVFoundation/constants.m`, which builds the endpoint, appends a nonce comment -to one first-party source, rebuilds, counts builders that RAN, and reverts: - - counter self test ran=1, so the counter can report non-zero - baseline ran=1 in 937.5 s (the endpoint was warm) - after editing one .m ran=2 in 1276.6 s - cider-buck2-skeleton - cider-buck2-sources - of the 1,474 groups ZERO - ld64 did not rebuild - graph did not rebuild - -**THE POSITIVE CONTROL IS THE POINT, because a zero needs one.** `cider-buck2-sources` -rebuilding proves the edit REACHED `projectSrc`. Without that, ran=2 would be indistinguishable -from a probe that measured nothing, and the file was checked against the source filter -beforehand for the same reason: `nix/lib/cider-src.nix` excludes only top-level names plus every -file called `BUCK`, and `darwin/` is kept. - -**WHAT THE TWO REMAINING BUILDERS ARE.** Not a cascade: `projectSrc` is an input to the graph -derivation, so when a source changes it must be re-derived. Two derivations and a re-resolution -is the floor, not a defect. - -**WHAT THIS SUPERSEDES.** The figure on record was "323 compiles and still climbing" from a -probe predating #54, #74, #79 and #95. It is 2. And the entry's own stated cause was the wrong -code path: it blamed `stageProject` embedding the whole `projectSrc`, which is the -`sourceGroups` = OFF path, while every endpoint sets it ON and goes through `stageTextFor` plus -`pinStageLines`, whose pins resolve through `pinsTree` from frozen per-pin stores. #54 narrowed -the groups, #74 gave the pins a self-contained mirrored tree, #79 gave the escape destinations -their own store paths. Those three closed this between them; nothing here needed implementing. - -**TWO STALE CLAIMS THIS KILLED, both now corrected in place.** `nix/lib/cider-src.nix` justified -excluding BUCK files by "ld64 is built from this tree (nix/cctools-port.nix) ... 26,351 compile -steps", and `buck-quick-check.nu` predicted "6 builders, 17.5 minutes" with ld64 rebuilding. That -file no longer exists and #65 made ld64 the buck2-built linker. The BUCK exclusion RULE still -stands, on the smaller measured reason above. - -**NOTHING TO DO.** Do not implement the fix this entry used to propose. Re-run the probe if the -staging or source-filter code changes; that is what it is for. -### #66 - get the lowering out of the evaluator (DONE 2026-08-11) - -BOTH HALVES DONE. A general buck2-graph to dynamic-derivation bridge, worth having for OTHER -projects; cider is the first CONSUMER, not the target. - -**THE RESULT.** The whole 1,474-group graph builds through the emitted route and reproduces the -lowered route exactly. Final run: 4,516 builders, zero nix-level errors, all 1,474 producers and -all 1,474 emitted actions, and the per-group comparison reads - - OK the whole graph emitted, all 1474 group(s) match the lowered route - -**WHY THAT ZERO IS EVIDENCE.** The same check on the same graph reported `identical 1364, differ -110` one run earlier and named every differing group. So it had just demonstrated it can report -a non-zero, and 110 to 0 is the #95 migcom fix landing. A zero from a check nobody has seen fail -would have been worth nothing. - -**KEEP BOTH ROUTES**, for an empirical reason rather than an aesthetic one: the DIFFERENTIAL -between them found four real defects, two of which neither route could expose alone. A second -implementation that agrees is a test; deleting it converts a working check into an unverified -assumption. - -- **A, the bridge.** `nix/lib/dyn-actions.nix`, fourteen properties over eleven fixtures via - `scripts/buck-dyndrv-check.nu`. `scripts/buck-bridge-generality-check.nu` ENFORCES that the - reusable half references nothing outside itself, which is the requirement rather than a - nicety. Usage, constraints and the limits a real consumer found: `docs/dyn-actions-bridge.md`. -- **B, the adapter.** `cider-graph-specs` renders the builder script and - `cider-graph-specs` writes it plus `needs.json` and a `dyn/` spec dir inside the graph - derivation. The lowering no longer assembles a script; it reads the template. -- **STILL OPEN, and it is the user's call.** Making the adapter standalone means emitting the - toolchain list, the staging script and the staged tree scripts. Measured sizes: 1 toolchain - set shared by all 1,474, 94 distinct staging scripts, 3,013 staged tree scripts. Nothing - measured says what that would cost or save. - -Full detail, every measurement and the corrected claims: docs/plan-history.md, "#66 in detail" -and the 2026-08-12 heading. ### D — Correctness oracle (the keystone remaining) [ARCH-FREE] "It built" → "it built **correctly**." The project's core value proposition. @@ -456,6 +326,7 @@ and the 2026-08-12 heading. miscompile). **A codegen-class divergence is stop-the-line** — the shim is lying to the compiler (math, memory layout, or a syscall result) and everything above is suspect. + ### M1 tail (Phase C.3–C.4b) [ARCH-FREE] - Drive the official `pkgs.hello` **derivation** through guest nix (not hand-run configure/make). `scripts/build-pkg-bypass.nu ` generalizes to any nixpkgs @@ -465,6 +336,7 @@ and the 2026-08-12 heading. the checkout lifetime-pipe fd leak → pipe-page starvation, now FIXED; reverify if it recurs.) + ### E — Climb the package ladder [ARCH-FREE] - **E.1** dependency-weighted 26.05 x86_64-darwin target list (CLI-only; GUI *runtime* out of scope — building GUI apps against link-time framework stubs is fine). @@ -475,6 +347,7 @@ and the 2026-08-12 heading. - **Exit (campaign):** the full Tier-1..3 matrix green with oracle, on a frozen 26.05 pin, in CI, reproducibly from a clean prefix. + ### F — ARM readiness (prep only, do not start the port) [ARCH-PARAM] - **F.1** salvage-assess the three `feature/arm-support*` branches → `PLAN.md`. - **F.2** arch-boundary audit (syscall numbers, ucontext layouts, asm, page size). Audit @@ -486,6 +359,7 @@ and the 2026-08-12 heading. - **F.4** document the QEMU aarch64 dev recipe (share `/nix/store` via virtiofs; never run ciderd under qemu-user — signal/TLS fidelity). + ### CI + remote builder (built in Campaign 1, unvalidated — needs rework) Machinery exists but was **never validated end-to-end on a live prefix** and predates the Rust rewrite / launchd-bypass / 26.05 pin / submodule removal: @@ -496,6 +370,7 @@ Rust rewrite / launchd-bypass / 26.05 pin / submodule removal: `nix.buildMachines` → sshd in Darling → guest nix-daemon, shared store avoids SSH copy) is the north star but unexercised — and conflicts with one-command-per-container. + ### Performance (measure during E; acceptable-if-slow for CI) Baseline: spawn ~11–12× native (~28 ms/proc), compute ~7.6×. Spawn tax: ~22 ms (78%) = the daemon fork/exec/RPC path. Landed and done: P0 ucred cache, P1 sigmask-free context switch, @@ -513,6 +388,7 @@ P2 epoll re-arm memoize. `__ulock_wait/wake`→`futex(2)`, `vchroot_expand` path translation, cached `mach_task_self`/`mach_host_self`, getpwuid via glibc NSS. + ### Watch-items (reopen on demand) **Content addressing is research-grade, and the invalidation design rests on it.** Early @@ -536,6 +412,7 @@ to "still crash on trivial cases as recently as Nix 2.34.x", which is the versio - **SIGFPE exec-fidelity flake (#44):** intermittent signal-8 in guest build/test binaries — retryable (nix build ×4), not a real error nor a Rust regression. + ### Upstream adoption Fork point `f39a29489` (2026-03); upstream idle on core as of 2026-07-19. Adopt only when a concrete failure justifies it: @@ -704,6 +581,7 @@ And the reference build is a wasting asset: gen-mig-from-ninja.py, gen-buck-from and cider-install-from-manifests all read it, and it disappears when cmake does, so every generator needs to be re-runnable before that happens. + ### Stage 1 and stage 2 queue: FINISHED, moved to docs/plan-history.md 634 lines lived here, 45 percent of this file, and every numbered item in them is a @@ -718,25 +596,6 @@ measurements, which are real and were expensive to take. THE OPEN TASKS ARE #97, #98, #99, #76 AND #92. #96 is DONE (both routes). Nothing else in this file is a queue. -### #97 to #99: porting buck2 into the overby.me monorepo, which takes no Python - -**#99 IS DONE as of 2026-08-12.** All 2,244 lines of build-path Python are Rust: the graph dump, -the spec lowering, the source closure, the skeletoniser, the buck-src normaliser and the -dynamic-derivation spec fixup. Each flip was verified by rebuilding the content addressed -artifact it feeds and comparing STORE PATHS, which for a CA output is a byte comparison: the -graph, the specs, the sources and the skeleton all came out identical. Nothing on the build path -is interpreted now except the buck2 rules' own python, which is upstream's. - -**See `docs/monorepo-port.md`**, which carries the measurements. The headline from #97: the eight -`gen-*.py` scripts are 5,338 lines, and the premise that the whole class is dead-end reference -reading is WRONG FOR HALF OF IT. Four are live generators over live inputs, and the biggest file -is not a tool at all but a LIBRARY that four live checks import, of which they use 7 percent -(8 functions, 182 lines of 2,508). Archive 2,743 lines, port 2,595. - -The measurement trap worth keeping: grepping for a generator's name finds 2,899 hits for -`gen-buck-from-ninja.py`, and nearly all are `# GENERATED by scripts/X` headers in the files it -WROTE. A provenance header is not a call site, and counting it as one makes every generator look -live. ### Darwin Rust: route A builds and RUNS, and dies in dyld lazy binding (harness task #96) @@ -806,6 +665,7 @@ SIGSEGV became exit 0, `std::env::consts::OS` reporting macos. bash survived the same stack only because its startup high-water mark is lower. + ### Route B: the official Darwin rustc under cider (DONE, it compiles Rust in the guest) The rustc component is fetched and installed into the guest at `/opt/rustc` (442 MB). Running it @@ -868,6 +728,7 @@ not installed). `--emit=obj` therefore closes it end to end: 9,376 bytes, carrying `__ZN3std2rt10lang_start...` and the `CIDER_RUSTC_GUEST_OK map=` marker string. A Darwin rustc running under cider compiled Rust to Darwin object code. + ### @rpath was never resolved, because mldr handed dyld the HOST path (FIXED) `dyld: Library not loaded: @rpath/librustc_driver-...dylib`, with the dylib sitting at @@ -909,193 +770,60 @@ this stayed invisible until a binary that uses `@rpath` ran. guest needs a Mach-O `ld64` there (route A links on the host instead). rustc reaches for `cc` and `/Library/Developer/DarlingCLT/usr/bin/clang` does not exist. -### #76 - the Darling-origin host tools in Rust (DONE except two that are toolchain-blocked) -Written 2026-08-12 because the task was open in the harness with NO PLAN entry, so the next -increment would have rediscovered all of the below. Read off the tree, not from memory. +--- -**TWO OF THEM ARE ALREADY PORTED, and well.** `linux/buildtools/BUCK` gives the CANONICAL names -`getuuid` and `elfdep` to `rust_binary` targets over `getuuid.rs` and `elfdep.rs`; the C survives -as `getuuid_c` and `elfdep_c` deliberately, because `scripts/buck-hosttools-parity.nu` diffs the -two on the same inputs and that comparison is only re-runnable while both exist. Provenance was -checked: both are Darling-origin and GPL (getuuid.c Copyright 2018 Lubos Dolezel, elfdep.c -2018-2020), so the headers stay and a Cider line sits beside them. +## Task history, newest first -Parity is BYTE parity, stdout as hex and stderr verbatim, which is not pedantry: writing the -ports from a reading of the C produced a defect that survived review and died there, because -`std::io::Error` renders ENOENT as "No such file or directory (os error 2)" while C `strerror` -prints only "No such file or directory". Eyes would have passed it. +### #102 - GUEST Rust: buck2 can build Mach-O Rust binaries now -**NOTHING IN THE BUCK2 BUILD CONSUMES EITHER TOOL.** The one consumer, `cmake/dsym.cmake`, -belonged to the reference CMake build this port replaced. So the `_c` targets cost one compile -each and nothing else, and they can be dropped whenever the comparison stops being worth -re-running. +The user asked for this on 2026-08-12 after #76 left xcrun and PlistBuddy described as blocked. +That description was too strong: #96 route A had ALREADY cross-compiled a full std Rust program +and run it in cider. What was missing was never the ability, it was the plumbing, and this entry +is what closed it. -**AND UNTIL 2026-08-12 NOTHING INVOKED THE PARITY CHECK EITHER**, which is the same gap #85 found -in the lowering stage check: a check nobody runs is a file, not a check. `scripts/buck-test.nu` -now builds the six binaries in one buck2 call and runs it in the wrapgen section, so a divergence -fails the suite instead of waiting for someone to remember the script exists. +**THE DESIGN TURNED ON THE CRATE TYPE, and it was measured rather than argued.** -**WHAT IS ACTUALLY LEFT, and it is smaller than the task title suggests:** + --emit=obj a std hello leaves 11 Rust symbols undefined + --emit=obj -C lto=fat WORSE, 14, because the allocator shims join them + --crate-type staticlib of the 191 symbols the archive cannot satisfy, ZERO are Rust-mangled - bsdln NOT GONE, and NOT ours to rewrite. This entry said "GONE, zero files match - anywhere in the tree" and that was false: linux/bsdln/{BUCK,ln.c} is right - there, //linux/bsdln:bsdln builds, and linux/bsdln/BUCK already records the - decision. ln.c is Copyright 1987, 1993, 1994 The Regents of the University of - California, so it is FreeBSD ln with a small local delta, not Darling-origin - code. The intended treatment is DE-VENDORING (pin the upstream source, compile - it with -include bsd/string.h), not a Rust rewrite that would throw away the - upstream history. - HOW THE FALSE CLAIM SURVIVED, because the shape recurs: the search matched - FILE NAMES only. Nothing is called bsdln.c; the directory is called bsdln. Same - family as the buck-src lesson, where a search was structurally unable to see - what it was asked about, so it returned a confident zero. - wrapgen DONE 2026-08-12. linux/libelfloader/wrapgen/wrapgen.rs is the build tool now - and the C++ is kept beside it as wrapgen_c. Provenance was the last blocker and - it is resolved; the record of how is below because the method is reusable. - THE PROBLEM WAS that all four files carry no copyright, no licence and no - Darling marker, and local history cannot settle it either: jj file annotate - attributes every line to the #87 stage 1a move and the log for the path holds - that one commit, so the pre-move history was squashed. - RESOLVED BY BLOB IDENTITY AGAINST UPSTREAM, which is exact rather than a - judgement. Darling has these at src/libelfloader/wrapgen/, and a git blob - hash is sha1 over "blob \\0" plus content, so it can be computed locally - and compared with what the GitHub API reports: +The 191 are libSystem C symbols, malloc and pthread_ and __NSGetArgv and dispatch_, which is +exactly what //buck-src:system_final already gives every C guest binary. So the archive drops +into darwin_binary as an ordinary `objs` entry and NOTHING about the link path changes: no +-syslibroot, no dependency on a built prefix, and dylibs arrive as +-Wl,-dylib_file,: the way they always do. - print_wrapped_elf.cpp 3829 B IDENTICAL c12c01045ee7 - produce_stubs_example.h 156 B IDENTICAL b59774dba4f5 - wrapgen.cpp 7830 B IDENTICAL 7079e613ecf6 - stubgen32.cpp 8646 B differs by exactly our own rename +**THE ENTRY POINT IS C.** A staticlib has no main, so a guest Rust tool exports +`#[unsafe(no_mangle)] pub extern "C" fn main(argc, argv)` and crt1.10.6 finds it. Rust lang_start +does NOT run: no stack-overflow guard message, no std-installed cleanup. std itself works, and +darwin/rustprobe is written to prove it rather than assume it, because on macOS args come from +_NSGetArgv rather than an init hook: the probe asserts std::env::args matches the argc crt1 was +given, and prints through std::io. - THE ONE DELTA IS FULLY ACCOUNTED FOR. stubgen32.cpp is 10 bytes shorter than - upstream because #84 renamed 5 occurrences of _darling_elfcalls to - _cider_elfcalls, and 5 sites times 2 characters is exactly 10. Reversing that - substitution reproduces upstream blob d1acd8c0d1aa EXACTLY, so nothing else - differs. That is a proof rather than an inspection. - SO: Darling-origin, unmodified apart from our rename, and the missing header - is UPSTREAM's absence which we inherit rather than something the move lost. + cider-rust-probe argc=1 env_args=1 + PASS: a Rust binary built for Darwin RAN inside cider - **BUT IT IS NOT THE SAME FOOTING AS getuuid AND elfdep, AND THAT IS THE POINT - WORTH CARRYING.** Those two are consumed by NOTHING in the buck2 build, which - is exactly why porting them was cheap and why a byte-parity harness over a - handful of inputs was sufficient proof. wrapgen is LOAD-BEARING: +**THE TOOLCHAIN IS PINNED, nix/darwinRust.nix.** nixpkgs cannot supply it and the refusal is not +about Rust: asking the pinned rev for a linux-to-darwin cross set dies at +x86_64-apple-darwin-cctools-1010.6, because Apple SDK pieces cannot be redistributed to non-Apple +hosts. Rust's own prebuilt darwin std can be, so this fetches the official rustc AND the official +rust-std with checksums. Both halves must come from the same release: crate metadata matching is +a STRING COMPARE, and the nixpkgs rustc appends "built from a source tarball" to an otherwise +identical 1.95.0/59807616e, which is E0514. Re-measured 2026-08-12, it still reproduces. Two +tarballs, not three: a sysroot holding ONLY the darwin target produces the same 17,694,176 byte +archive, so the host std would be 40 MB of nothing. - linux/libelfloader/BUCK:15 the target //linux/libelfloader:wrapgen - buck/rules/codegen.bzl:558 the elf_wrapper rule RUNS it at build time - darwin/CoreAudio/BUCK:38 consumer - linux/native/BUCK:74 consumer - buck-src/BUCK:49080 consumer, the generated one +**WHAT IS DONE AND WHAT IS NOT.** xcrun is ported and gated inside the container, five cases +identical in stdout, stderr and rc, reaching both branches (as xcrun the tool is NULLed; as cc it +is passed through). PlistBuddy is NOT started and is a different size of job: 1,277 lines with +193 CoreFoundation call sites, so it needs CF bindings before it needs a port. - It writes the Mach-O stub that lets a Darwin program call into a HOST ELF - library, and codegen.bzl:563 records that it dlopen()s the real .so AT BUILD - TIME. So the port had to reproduce its GENERATED OUTPUT exactly, across every - library any consumer feeds it, not merely behave the same on a few probes. +**A TRAP WORTH KEEPING, because it cost a red gate that looked like a broken port.** xcrun passes +getprogname() STRAIGHT THROUGH as the tool to invoke, so staging the two binaries as xcrun_c and +xcrun_rs made all five cases differ: one asked for a tool called xcrun_c, the other for xcrun_rs. +THE NAME IS AN INPUT to this program. Stage each under the real name in its own directory. - SO THE GATE WAS THE WHOLE CORPUS: all 22 libraries the three consumers name, - 16 from linux/native, 5 from darwin/CoreAudio and fuse from buck-src, compared - in the .c, the vars .h, stdout, stderr and exit code. Byte identical in every - one, from 3,615 bytes of C for swresample to 538,088 for GL, with 8 of the 22 - exercising the vars-header path. Five error paths too (no arguments, an - unloadable library, a non-ELF, a 32-bit ELF, an executable with no DT_SONAME), - plus a control that must fail and does. - - ONE BUG THE GATE CAUGHT THAT READING WOULD NOT HAVE: DT_SONAME is an index INTO - the string table, and the C++ still passes it through vaddr_to_offset before - adding it to a pointer that already includes the string table offset. The first - version read at the wrong offset and every soname came out as rubbish. - - THE ONE DELIBERATE DIFFERENCE, as in getuuid and elfdep: the C++ mmaps and casts - structures straight out of the mapping, so a lying offset reads past the end. - The Rust reads the file and parses at CHECKED offsets. - xcrun darwin/clt/xcrun.c, darwin/xcselect/xcrun.c, darwin/xcselect/xcrun-shim.c - PlistBuddy darwin/PlistBuddy/PlistBuddy.c - -**xcrun AND PlistBuddy ARE BLOCKED, and the reason is narrower than this entry first said.** -Both live under `darwin/`, so they are Mach-O GUEST binaries. Two separate gaps, measured -2026-08-12: - - buck/rules/rust.bzl zero occurrences of --target, so the rule cannot ask for a - non-host target even if one worked - rust-std for darwin ABSENT. This is the real blocker. - -**THE CORRECTION IS WORTH THE SPACE, because the first version of this entry said "a Darwin Rust -toolchain does not exist here" and that is false.** `rustc --print target-list` lists -`x86_64-apple-darwin`, `aarch64-apple-darwin` and three more, so the COMPILER supports the -target. What is missing is the standard library built FOR it: - - rustc --target x86_64-apple-darwin hello.rs - error[E0463]: can't find crate for `std` - rustc --target x86_64-apple-darwin --emit=obj (with #![no_std]) - error[E0463]: can't find crate for `core` - -and `rustc --print target-libdir --target x86_64-apple-darwin` names a directory that does not -exist. `core` being absent too means even a freestanding object cannot be produced. - -So this is UNCONFIGURED, not impossible, and the distinction changes what the work is: supply -`core` and `std` for the target, then teach `rust.bzl` to pass `--target`. Note the project -already owns two of the awkward prerequisites, a Mach-O linker (ld64, buck2-built since #65) and -the Darwin SDK. Nobody has costed the rest. Do not start either tool expecting to finish it, but -do not repeat the claim that the toolchain cannot exist. - -So the honest shape of #76 is: THREE DONE with a byte-parity harness the suite now runs, one that -was never in scope (bsdln is upstream BSD and wants de-vendoring instead), and two blocked on a -missing toolchain. Nothing is left that is merely waiting to be picked up. - -**THE METHOD IS REUSABLE, and it is cheaper than reading history.** When provenance is in doubt -for a file that carries no header, do not argue from style or from a squashed log. Compute the -git blob hash locally, `sha1("blob " + len + "\0" + bytes)`, and compare it with what the GitHub -contents API reports for the upstream path. Identical means unmodified, full stop. When it -differs, reverse the known local transformation and hash again: if that reproduces the upstream -blob, the delta is fully explained and nothing is hiding in it. - -### #102 - GUEST Rust: buck2 can build Mach-O Rust binaries now - -The user asked for this on 2026-08-12 after #76 left xcrun and PlistBuddy described as blocked. -That description was too strong: #96 route A had ALREADY cross-compiled a full std Rust program -and run it in cider. What was missing was never the ability, it was the plumbing, and this entry -is what closed it. - -**THE DESIGN TURNED ON THE CRATE TYPE, and it was measured rather than argued.** - - --emit=obj a std hello leaves 11 Rust symbols undefined - --emit=obj -C lto=fat WORSE, 14, because the allocator shims join them - --crate-type staticlib of the 191 symbols the archive cannot satisfy, ZERO are Rust-mangled - -The 191 are libSystem C symbols, malloc and pthread_ and __NSGetArgv and dispatch_, which is -exactly what //buck-src:system_final already gives every C guest binary. So the archive drops -into darwin_binary as an ordinary `objs` entry and NOTHING about the link path changes: no --syslibroot, no dependency on a built prefix, and dylibs arrive as --Wl,-dylib_file,: the way they always do. - -**THE ENTRY POINT IS C.** A staticlib has no main, so a guest Rust tool exports -`#[unsafe(no_mangle)] pub extern "C" fn main(argc, argv)` and crt1.10.6 finds it. Rust lang_start -does NOT run: no stack-overflow guard message, no std-installed cleanup. std itself works, and -darwin/rustprobe is written to prove it rather than assume it, because on macOS args come from -_NSGetArgv rather than an init hook: the probe asserts std::env::args matches the argc crt1 was -given, and prints through std::io. - - cider-rust-probe argc=1 env_args=1 - PASS: a Rust binary built for Darwin RAN inside cider - -**THE TOOLCHAIN IS PINNED, nix/darwinRust.nix.** nixpkgs cannot supply it and the refusal is not -about Rust: asking the pinned rev for a linux-to-darwin cross set dies at -x86_64-apple-darwin-cctools-1010.6, because Apple SDK pieces cannot be redistributed to non-Apple -hosts. Rust's own prebuilt darwin std can be, so this fetches the official rustc AND the official -rust-std with checksums. Both halves must come from the same release: crate metadata matching is -a STRING COMPARE, and the nixpkgs rustc appends "built from a source tarball" to an otherwise -identical 1.95.0/59807616e, which is E0514. Re-measured 2026-08-12, it still reproduces. Two -tarballs, not three: a sysroot holding ONLY the darwin target produces the same 17,694,176 byte -archive, so the host std would be 40 MB of nothing. - -**WHAT IS DONE AND WHAT IS NOT.** xcrun is ported and gated inside the container, five cases -identical in stdout, stderr and rc, reaching both branches (as xcrun the tool is NULLed; as cc it -is passed through). PlistBuddy is NOT started and is a different size of job: 1,277 lines with -193 CoreFoundation call sites, so it needs CF bindings before it needs a port. - -**A TRAP WORTH KEEPING, because it cost a red gate that looked like a broken port.** xcrun passes -getprogname() STRAIGHT THROUGH as the tool to invoke, so staging the two binaries as xcrun_c and -xcrun_rs made all five cases differ: one asked for a tool called xcrun_c, the other for xcrun_rs. -THE NAME IS AN INPUT to this program. Stage each under the real name in its own directory. ### #102b - PlistBuddy: the measurement that had to come before the port @@ -1198,6 +926,7 @@ C dereference NULL: getWord returns NULL, nothing checks it, and parseType hands The port prints the message once and abandons the command, and the gate ASSERTS that difference rather than skipping the case. + ### #101 - dockur/macos: read it, and NOTHING transfers. Here is why, with the lines The user asked 2026-08-12 whether github.com/dockur/macos holds anything useful. Read at commit @@ -1265,6 +994,89 @@ work this way, so it is agreement rather than a lesson. of it.** The only lasting value is this entry, so nobody re-reads the repository hoping the SDK acquisition problem was solved over there. It was not; it was made someone else's problem. + +### #97 to #99: porting buck2 into the overby.me monorepo, which takes no Python + +**#99 IS DONE as of 2026-08-12.** All 2,244 lines of build-path Python are Rust: the graph dump, +the spec lowering, the source closure, the skeletoniser, the buck-src normaliser and the +dynamic-derivation spec fixup. Each flip was verified by rebuilding the content addressed +artifact it feeds and comparing STORE PATHS, which for a CA output is a byte comparison: the +graph, the specs, the sources and the skeleton all came out identical. Nothing on the build path +is interpreted now except the buck2 rules' own python, which is upstream's. + +**See `docs/monorepo-port.md`**, which carries the measurements. The headline from #97: the eight +`gen-*.py` scripts are 5,338 lines, and the premise that the whole class is dead-end reference +reading is WRONG FOR HALF OF IT. Four are live generators over live inputs, and the biggest file +is not a tool at all but a LIBRARY that four live checks import, of which they use 7 percent +(8 functions, 182 lines of 2,508). Archive 2,743 lines, port 2,595. + +The measurement trap worth keeping: grepping for a generator's name finds 2,899 hits for +`gen-buck-from-ninja.py`, and nearly all are `# GENERATED by scripts/X` headers in the files it +WROTE. A provenance header is not a call site, and counting it as one makes every generator look +live. + + +### #96 - a first-party source edit and the group cascade (ANSWERED 2026-08-12: THERE IS NO CASCADE) + +**THE PREMISE IN THE OLD TITLE WAS FALSE.** It read "a first-party source edit still +invalidates every group that stages the project". Measured on the endpoint, it invalidates +**zero** groups. + +**THE MEASUREMENT.** `scripts/buck-quick-check.nu --attr .#cider-buck2-prefix-min --probe +darwin/frameworks/AVFoundation/constants.m`, which builds the endpoint, appends a nonce comment +to one first-party source, rebuilds, counts builders that RAN, and reverts: + + counter self test ran=1, so the counter can report non-zero + baseline ran=1 in 937.5 s (the endpoint was warm) + after editing one .m ran=2 in 1276.6 s + cider-buck2-skeleton + cider-buck2-sources + of the 1,474 groups ZERO + ld64 did not rebuild + graph did not rebuild + +**THE POSITIVE CONTROL IS THE POINT, because a zero needs one.** `cider-buck2-sources` +rebuilding proves the edit REACHED `projectSrc`. Without that, ran=2 would be indistinguishable +from a probe that measured nothing, and the file was checked against the source filter +beforehand for the same reason: `nix/lib/cider-src.nix` excludes only top-level names plus every +file called `BUCK`, and `darwin/` is kept. + +**WHAT THE TWO REMAINING BUILDERS ARE.** Not a cascade: `projectSrc` is an input to the graph +derivation, so when a source changes it must be re-derived. Two derivations and a re-resolution +is the floor, not a defect. + +**WHAT THIS SUPERSEDES.** The figure on record was "323 compiles and still climbing" from a +probe predating #54, #74, #79 and #95. It is 2. And the entry's own stated cause was the wrong +code path: it blamed `stageProject` embedding the whole `projectSrc`, which is the +`sourceGroups` = OFF path, while every endpoint sets it ON and goes through `stageTextFor` plus +`pinStageLines`, whose pins resolve through `pinsTree` from frozen per-pin stores. #54 narrowed +the groups, #74 gave the pins a self-contained mirrored tree, #79 gave the escape destinations +their own store paths. Those three closed this between them; nothing here needed implementing. + +**TWO STALE CLAIMS THIS KILLED, both now corrected in place.** `nix/lib/cider-src.nix` justified +excluding BUCK files by "ld64 is built from this tree (nix/cctools-port.nix) ... 26,351 compile +steps", and `buck-quick-check.nu` predicted "6 builders, 17.5 minutes" with ld64 rebuilding. That +file no longer exists and #65 made ld64 the buck2-built linker. The BUCK exclusion RULE still +stands, on the smaller measured reason above. + +**NOTHING TO DO.** Do not implement the fix this entry used to propose. Re-run the probe if the +staging or source-filter code changes; that is what it is for. + +### #95 - migcom stamped the build time into every stub, so 110 groups never cached (DONE) + +migcom wrote the wall clock into every generated stub, so two builds of the same inputs at +different times produced different bytes, and under content addressing that defeated early +cutoff for everything downstream of a mig group. Found by #66's full-graph comparison: 1,364 of +1,474 groups identical, 110 differing, all 110 mig. + +Fixed by `patches/bootstrap_cmds/0001-migcom-honour-source-date-epoch.patch`. A freshly built +mig group now reads `stub generated Tue Jan 1 00:00:00 1980`. Guarded by +`scripts/buck-mig-epoch-check.nu`, which builds ONE mig group instead of the whole graph and is +in `buck-test.nu`. + +Full detail, including the copy I got wrong first: docs/plan-history.md, "#95 in detail". + + ### #92 - read the graph through buck2 structured data, not its rendered output Written 2026-08-12 for the same reason as #76: the task was open with no entry describing it. @@ -1326,3 +1138,180 @@ configure_file now passes its values through a file so that one is safe BY CONST than by vigilance. The cheap structured source does not exist, the middle route (drive everything through what-ran) requires every action to execute in the dumping invocation, and the titled route is a vendoring project. None of that is worth paying to remove a guarded assumption. + + +### #76 - the Darling-origin host tools in Rust (DONE except two that are toolchain-blocked) + +Written 2026-08-12 because the task was open in the harness with NO PLAN entry, so the next +increment would have rediscovered all of the below. Read off the tree, not from memory. + +**TWO OF THEM ARE ALREADY PORTED, and well.** `linux/buildtools/BUCK` gives the CANONICAL names +`getuuid` and `elfdep` to `rust_binary` targets over `getuuid.rs` and `elfdep.rs`; the C survives +as `getuuid_c` and `elfdep_c` deliberately, because `scripts/buck-hosttools-parity.nu` diffs the +two on the same inputs and that comparison is only re-runnable while both exist. Provenance was +checked: both are Darling-origin and GPL (getuuid.c Copyright 2018 Lubos Dolezel, elfdep.c +2018-2020), so the headers stay and a Cider line sits beside them. + +Parity is BYTE parity, stdout as hex and stderr verbatim, which is not pedantry: writing the +ports from a reading of the C produced a defect that survived review and died there, because +`std::io::Error` renders ENOENT as "No such file or directory (os error 2)" while C `strerror` +prints only "No such file or directory". Eyes would have passed it. + +**NOTHING IN THE BUCK2 BUILD CONSUMES EITHER TOOL.** The one consumer, `cmake/dsym.cmake`, +belonged to the reference CMake build this port replaced. So the `_c` targets cost one compile +each and nothing else, and they can be dropped whenever the comparison stops being worth +re-running. + +**AND UNTIL 2026-08-12 NOTHING INVOKED THE PARITY CHECK EITHER**, which is the same gap #85 found +in the lowering stage check: a check nobody runs is a file, not a check. `scripts/buck-test.nu` +now builds the six binaries in one buck2 call and runs it in the wrapgen section, so a divergence +fails the suite instead of waiting for someone to remember the script exists. + +**WHAT IS ACTUALLY LEFT, and it is smaller than the task title suggests:** + + bsdln NOT GONE, and NOT ours to rewrite. This entry said "GONE, zero files match + anywhere in the tree" and that was false: linux/bsdln/{BUCK,ln.c} is right + there, //linux/bsdln:bsdln builds, and linux/bsdln/BUCK already records the + decision. ln.c is Copyright 1987, 1993, 1994 The Regents of the University of + California, so it is FreeBSD ln with a small local delta, not Darling-origin + code. The intended treatment is DE-VENDORING (pin the upstream source, compile + it with -include bsd/string.h), not a Rust rewrite that would throw away the + upstream history. + HOW THE FALSE CLAIM SURVIVED, because the shape recurs: the search matched + FILE NAMES only. Nothing is called bsdln.c; the directory is called bsdln. Same + family as the buck-src lesson, where a search was structurally unable to see + what it was asked about, so it returned a confident zero. + wrapgen DONE 2026-08-12. linux/libelfloader/wrapgen/wrapgen.rs is the build tool now + and the C++ is kept beside it as wrapgen_c. Provenance was the last blocker and + it is resolved; the record of how is below because the method is reusable. + THE PROBLEM WAS that all four files carry no copyright, no licence and no + Darling marker, and local history cannot settle it either: jj file annotate + attributes every line to the #87 stage 1a move and the log for the path holds + that one commit, so the pre-move history was squashed. + RESOLVED BY BLOB IDENTITY AGAINST UPSTREAM, which is exact rather than a + judgement. Darling has these at src/libelfloader/wrapgen/, and a git blob + hash is sha1 over "blob \\0" plus content, so it can be computed locally + and compared with what the GitHub API reports: + + print_wrapped_elf.cpp 3829 B IDENTICAL c12c01045ee7 + produce_stubs_example.h 156 B IDENTICAL b59774dba4f5 + wrapgen.cpp 7830 B IDENTICAL 7079e613ecf6 + stubgen32.cpp 8646 B differs by exactly our own rename + + THE ONE DELTA IS FULLY ACCOUNTED FOR. stubgen32.cpp is 10 bytes shorter than + upstream because #84 renamed 5 occurrences of _darling_elfcalls to + _cider_elfcalls, and 5 sites times 2 characters is exactly 10. Reversing that + substitution reproduces upstream blob d1acd8c0d1aa EXACTLY, so nothing else + differs. That is a proof rather than an inspection. + SO: Darling-origin, unmodified apart from our rename, and the missing header + is UPSTREAM's absence which we inherit rather than something the move lost. + + **BUT IT IS NOT THE SAME FOOTING AS getuuid AND elfdep, AND THAT IS THE POINT + WORTH CARRYING.** Those two are consumed by NOTHING in the buck2 build, which + is exactly why porting them was cheap and why a byte-parity harness over a + handful of inputs was sufficient proof. wrapgen is LOAD-BEARING: + + linux/libelfloader/BUCK:15 the target //linux/libelfloader:wrapgen + buck/rules/codegen.bzl:558 the elf_wrapper rule RUNS it at build time + darwin/CoreAudio/BUCK:38 consumer + linux/native/BUCK:74 consumer + buck-src/BUCK:49080 consumer, the generated one + + It writes the Mach-O stub that lets a Darwin program call into a HOST ELF + library, and codegen.bzl:563 records that it dlopen()s the real .so AT BUILD + TIME. So the port had to reproduce its GENERATED OUTPUT exactly, across every + library any consumer feeds it, not merely behave the same on a few probes. + + SO THE GATE WAS THE WHOLE CORPUS: all 22 libraries the three consumers name, + 16 from linux/native, 5 from darwin/CoreAudio and fuse from buck-src, compared + in the .c, the vars .h, stdout, stderr and exit code. Byte identical in every + one, from 3,615 bytes of C for swresample to 538,088 for GL, with 8 of the 22 + exercising the vars-header path. Five error paths too (no arguments, an + unloadable library, a non-ELF, a 32-bit ELF, an executable with no DT_SONAME), + plus a control that must fail and does. + + ONE BUG THE GATE CAUGHT THAT READING WOULD NOT HAVE: DT_SONAME is an index INTO + the string table, and the C++ still passes it through vaddr_to_offset before + adding it to a pointer that already includes the string table offset. The first + version read at the wrong offset and every soname came out as rubbish. + + THE ONE DELIBERATE DIFFERENCE, as in getuuid and elfdep: the C++ mmaps and casts + structures straight out of the mapping, so a lying offset reads past the end. + The Rust reads the file and parses at CHECKED offsets. + xcrun darwin/clt/xcrun.c, darwin/xcselect/xcrun.c, darwin/xcselect/xcrun-shim.c + PlistBuddy darwin/PlistBuddy/PlistBuddy.c + +**xcrun AND PlistBuddy ARE BLOCKED, and the reason is narrower than this entry first said.** +Both live under `darwin/`, so they are Mach-O GUEST binaries. Two separate gaps, measured +2026-08-12: + + buck/rules/rust.bzl zero occurrences of --target, so the rule cannot ask for a + non-host target even if one worked + rust-std for darwin ABSENT. This is the real blocker. + +**THE CORRECTION IS WORTH THE SPACE, because the first version of this entry said "a Darwin Rust +toolchain does not exist here" and that is false.** `rustc --print target-list` lists +`x86_64-apple-darwin`, `aarch64-apple-darwin` and three more, so the COMPILER supports the +target. What is missing is the standard library built FOR it: + + rustc --target x86_64-apple-darwin hello.rs + error[E0463]: can't find crate for `std` + rustc --target x86_64-apple-darwin --emit=obj (with #![no_std]) + error[E0463]: can't find crate for `core` + +and `rustc --print target-libdir --target x86_64-apple-darwin` names a directory that does not +exist. `core` being absent too means even a freestanding object cannot be produced. + +So this is UNCONFIGURED, not impossible, and the distinction changes what the work is: supply +`core` and `std` for the target, then teach `rust.bzl` to pass `--target`. Note the project +already owns two of the awkward prerequisites, a Mach-O linker (ld64, buck2-built since #65) and +the Darwin SDK. Nobody has costed the rest. Do not start either tool expecting to finish it, but +do not repeat the claim that the toolchain cannot exist. + +So the honest shape of #76 is: THREE DONE with a byte-parity harness the suite now runs, one that +was never in scope (bsdln is upstream BSD and wants de-vendoring instead), and two blocked on a +missing toolchain. Nothing is left that is merely waiting to be picked up. + +**THE METHOD IS REUSABLE, and it is cheaper than reading history.** When provenance is in doubt +for a file that carries no header, do not argue from style or from a squashed log. Compute the +git blob hash locally, `sha1("blob " + len + "\0" + bytes)`, and compare it with what the GitHub +contents API reports for the upstream path. Identical means unmodified, full stop. When it +differs, reverse the known local transformation and hash again: if that reproduces the upstream +blob, the delta is fully explained and nothing is hiding in it. + + +### #66 - get the lowering out of the evaluator (DONE 2026-08-11) + +BOTH HALVES DONE. A general buck2-graph to dynamic-derivation bridge, worth having for OTHER +projects; cider is the first CONSUMER, not the target. + +**THE RESULT.** The whole 1,474-group graph builds through the emitted route and reproduces the +lowered route exactly. Final run: 4,516 builders, zero nix-level errors, all 1,474 producers and +all 1,474 emitted actions, and the per-group comparison reads + + OK the whole graph emitted, all 1474 group(s) match the lowered route + +**WHY THAT ZERO IS EVIDENCE.** The same check on the same graph reported `identical 1364, differ +110` one run earlier and named every differing group. So it had just demonstrated it can report +a non-zero, and 110 to 0 is the #95 migcom fix landing. A zero from a check nobody has seen fail +would have been worth nothing. + +**KEEP BOTH ROUTES**, for an empirical reason rather than an aesthetic one: the DIFFERENTIAL +between them found four real defects, two of which neither route could expose alone. A second +implementation that agrees is a test; deleting it converts a working check into an unverified +assumption. + +- **A, the bridge.** `nix/lib/dyn-actions.nix`, fourteen properties over eleven fixtures via + `scripts/buck-dyndrv-check.nu`. `scripts/buck-bridge-generality-check.nu` ENFORCES that the + reusable half references nothing outside itself, which is the requirement rather than a + nicety. Usage, constraints and the limits a real consumer found: `docs/dyn-actions-bridge.md`. +- **B, the adapter.** `cider-graph-specs` renders the builder script and + `cider-graph-specs` writes it plus `needs.json` and a `dyn/` spec dir inside the graph + derivation. The lowering no longer assembles a script; it reads the template. +- **STILL OPEN, and it is the user's call.** Making the adapter standalone means emitting the + toolchain list, the staging script and the staged tree scripts. Measured sizes: 1 toolchain + set shared by all 1,474, 94 distinct staging scripts, 3,013 staged tree scripts. Nothing + measured says what that would cost or save. + +Full detail, every measurement and the corrected claims: docs/plan-history.md, "#66 in detail" +and the 2026-08-12 heading. diff --git a/docs/darwin-builder.md b/docs/darwin-builder.md index c624270ce..227e64e8a 100644 --- a/docs/darwin-builder.md +++ b/docs/darwin-builder.md @@ -572,7 +572,7 @@ cider shell bash -lc 'nix-build ...' 2>&1 | grep -i "unimplemented\|STUB" ./scripts/triage-syscalls.nu --output /tmp/triage.md # Check the known triage table -cat PLAN.md +cat changelog.md ``` If you discover a new unimplemented syscall, please @@ -699,13 +699,13 @@ See [Performance Tuning](#performance-tuning) above. The most common causes are: | **cider-build-hook** | Alternative to SSH — invokes `cider shell` directly | | **ciderBuilderModule.nix** | NixOS module that wires everything together declaratively | -For more technical details, see [PLAN.md](../PLAN.md). +For more technical details, see [changelog.md](../changelog.md). --- ## Further Reading -- [Project plan](../PLAN.md) — development plan, known blockers and the syscall triage table +- [Project plan](../changelog.md) — development plan, known blockers and the syscall triage table - [Darling documentation](https://docs.darlinghq.org/) — upstream Darling docs - [Nix remote builders](https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html) — Nix manual on distributed builds - [Blog: Nix All The Way Down](https://ersei.net/en/blog/nix-all-the-way-down) — early exploration of Nix-in-Darling \ No newline at end of file diff --git a/docs/plan-buck2-port.md b/docs/plan-buck2-port.md index e019d6570..88da4eccf 100644 --- a/docs/plan-buck2-port.md +++ b/docs/plan-buck2-port.md @@ -60,7 +60,7 @@ risk register were removed once they described finished work, along with the wri of the coverage blind spot, the install rules, the four boot failures and the endpoint milestone: each of those is the commit that made the change. -What is still true and current lives in the root `PLAN.md`. The two things below are +What is still true and current lives in the root `changelog.md`. The two things below are kept here because they are neither current status nor finished history. ## Profiling the evaluation: 152s of CPU down to 9s @@ -102,8 +102,8 @@ lowered target derivation**, a comment included. For an endpoint whose whole pur other people do not rebuild what they did not touch, that was its most expensive bug. Half fixed since. `nix/lib/ciderBuck2Lower.nix` filters the lowering source, so plan/, -docs/, nix/, scripts/, the VM tests, PLAN.md and the other documentation no longer relower -anything -- which matters because editing generators and PLAN.md is most of what this port +docs/, nix/, scripts/, the VM tests, changelog.md and the other documentation no longer relower +anything -- which matters because editing generators and changelog.md is most of what this port consists of. The precise fix is still open and is task #11: depend on the SOURCES THE ACTIONS NAME, which the graph already records per action, rather than on a filtered whole-project path. diff --git a/docs/plan-history.md b/docs/plan-history.md index 7910e291b..b93563009 100644 --- a/docs/plan-history.md +++ b/docs/plan-history.md @@ -1,19 +1,19 @@ -# PLAN.md history: per-task post-mortems, archived 2026-08-11 +# changelog.md history: per-task post-mortems, archived 2026-08-11 -Moved out of PLAN.md verbatim so that file can be navigated. NOTHING HERE IS DELETED and +Moved out of changelog.md verbatim so that file can be navigated. NOTHING HERE IS DELETED and nothing is summarised: several of the fixes made on 2026-08-11 came out of post-mortems -written weeks earlier, so the detail is the point. What PLAN.md keeps is status, +written weeks earlier, so the detail is the point. What changelog.md keeps is status, architecture, the invariants, the open queue and the harness traps; what moved here is the per-task narrative of how each one was reached. -The durable RULES that were embedded in these narratives were hoisted back into PLAN.md +The durable RULES that were embedded in these narratives were hoisted back into changelog.md rather than left only here, so this file is provenance rather than something to consult before working. --- -## Archived from PLAN.md lines 28 to 634 +## Archived from changelog.md lines 28 to 634 ## Buck2 port: the order to work in @@ -626,7 +626,7 @@ ordinary revision bump in c99441f1, verified on both bisect targets plus rungs 1 --- -## Archived from PLAN.md lines 1759 to 1858 +## Archived from changelog.md lines 1759 to 1858 ## Grouped build: eval speed (done) vs incremental rebuild (open) — task #80 @@ -736,7 +736,7 @@ below it was written earlier the same day and its counts are of that moment: the in particular has moved from six to eleven, and the sentence saying so is left as written rather than edited, because this file is a record of what was known when. -Moved out of PLAN.md to keep it short. This is the working record: what was measured, what +Moved out of changelog.md to keep it short. This is the working record: what was measured, what turned out false, and the traps that cost time. ### #66 — get the lowering out of the evaluator @@ -995,7 +995,7 @@ cider-shaped in here" were never a check. ## #95 in detail (2026-08-11) -Moved out of PLAN.md to keep it short. +Moved out of changelog.md to keep it short. ### #95 - migcom stamps the build time into every stub, so 110 groups never cache @@ -1082,9 +1082,9 @@ the staging script through `CIDER_STAGE`, so both routes rebuilt from there. So the rebuild was real and necessary, but its cause was the source tree rather than the graph, and a re-run of the port checks after it compares against the same graph as before. -## The stage 1 and stage 2 queue, moved out of PLAN.md 2026-08-12 +## The stage 1 and stage 2 queue, moved out of changelog.md 2026-08-12 -MOVED RATHER THAN DELETED, and it is 634 lines, which was 45 percent of PLAN.md. Every +MOVED RATHER THAN DELETED, and it is 634 lines, which was 45 percent of changelog.md. Every numbered item in it is a finished task: the stock switch and the GUI frameworks (#18 to #22), the nine unported cli edges (#21), the host tools (#8), the NixOS VM (#10, #12), the daemon growth traps (#48), the relative escape findings (#74, #79, now guarded by @@ -1167,7 +1167,7 @@ has been true six times running, each time a check freshly written. the BUILD tree only -- `buck/ src/ darwin/ linux/ tests/ etc/ misc/ patches/ templates/ tools/ buck-src/ buck-rust/` plus the root dotfiles. (`cmake/` was in this list until #82 removed cmake; it no longer exists.) `scripts/`, `nix/`, - `docs/`, `plan/`, `PLAN.md` and `flake.nix` are NOT in it, with three exceptions that + `docs/`, `plan/`, `changelog.md` and `flake.nix` are NOT in it, with three exceptions that are their own inputs: `buck2-graph-dump.py`, `buck-src-normalise.py`, and `nix/lib/ciderBuck2{Graph,Lower}.nix`, which ARE the derivations. `scripts/buck-endpoint-stale.nu` answers this in a second, and it now takes the rule from @@ -1727,7 +1727,7 @@ currently un-tracked. The task #80 grouped-build eval-speed analysis moved to `docs/plan-history.md`. -## #66 full entry as it stood when the task closed, moved out of PLAN.md 2026-08-12 +## #66 full entry as it stood when the task closed, moved out of changelog.md 2026-08-12 The PLAN entry is now a summary. This is what it said in full, kept for the measurements and for the two claims it records as WRONG, which are the useful part. diff --git a/docs/upstream-mapping.md b/docs/upstream-mapping.md index 0ba5d3e46..f3c2b6d1b 100644 --- a/docs/upstream-mapping.md +++ b/docs/upstream-mapping.md @@ -105,7 +105,7 @@ upstream change for its *intent* and apply that intent to the Rust. - `darwin/dirserv/`, `darwin/sandbox-exec/` -- first-party additions - `buck/`, `buck-src/`, `buck-rust/` -- the buck2 build (the point of this fork) - `nix/`, `flake.nix`, `flake.lock` -- the Nix endpoints -- `scripts/`, `docs/`, `plan/`, `PLAN.md`, `templates/`, `tools/`, `patches/` +- `scripts/`, `docs/`, `plan/`, `changelog.md`, `templates/`, `tools/`, `patches/` ## Applying an upstream change @@ -150,6 +150,6 @@ pick, and it produced one false "missing" during this triage. ## What "up to date with upstream" means here Build parity is measured against the **reference cmake build at the fork point**: 151 of 151 -link edges. That number says nothing about the 36 upstream commits since. See PLAN.md, *What +link edges. That number says nothing about the 36 upstream commits since. See changelog.md, *What 100 percent does NOT mean*, for the standing exclusions (32-bit, cctools from Nix, runtime parity unmeasured past dlopen). diff --git a/flake.nix b/flake.nix index 1e1c5f6fb..b6a6e5f1c 100644 --- a/flake.nix +++ b/flake.nix @@ -950,7 +950,7 @@ # ── Off git submodules: nix-pinned source tree ─────────────────── # # Darling's 147 vendored trees, assembled from fetchFromGitHub pins in - # nix/submodules.json instead of git submodules (see PLAN.md + # nix/submodules.json instead of git submodules (see docs/changelog.md # and nix/lib/cider-src.nix). Build WITHOUT ?submodules=1 -- cider-src # overlays every pinned submodule onto this flake's own tree and applies # patches//. Partial until every hash is filled @@ -1078,7 +1078,7 @@ # Initialise a new project with: # nix flake init -t github:nixie-dev/cider-nix#cider-builder # - # See: docs/darwin-builder.md, PLAN.md (Task 7.7) + # See: docs/darwin-builder.md, docs/changelog.md (Task 7.7) templates.cider-builder = { path = ./templates/cider-builder; description = "NixOS configuration with a Darling-based x86_64-darwin remote builder"; @@ -1111,7 +1111,7 @@ # nix build .#checks.x86_64-linux.nix-in-cider -L # nix build .#checks.x86_64-linux.cider-builder -L # - # See: PLAN.md (Tasks 6.1, 6.2, 7.5) + # See: docs/changelog.md (Tasks 6.1, 6.2, 7.5) checks = pkgs: let # The buck2 MINIMAL build: it is the gate that actually finishes on this diff --git a/linux/buildtools/graph-specs/src/srcdeps.rs b/linux/buildtools/graph-specs/src/srcdeps.rs index ae15729ce..40e96febe 100644 --- a/linux/buildtools/graph-specs/src/srcdeps.rs +++ b/linux/buildtools/graph-specs/src/srcdeps.rs @@ -43,7 +43,7 @@ use std::process::ExitCode; // The lowering own exclusion list, so "coarse" here is the CURRENT baseline rather than the // unfiltered repo. Keep in step with nix/lib/ciderBuck2Lower.nix. const COARSE_EXCLUDE: &[&str] = &[ - "plan", "docs", "nix", "scripts", "PLAN.md", "README.md", "CONTRIBUTORS.md", + "plan", "docs", "nix", "scripts", "docs/changelog.md", "README.md", "CONTRIBUTORS.md", "LICENSE", ".vscode", ".claude", ".tangled", ".gdbinit", ".dfx-boot.log", ".git", ".jj", ".direnv", "buck-out", "result-graph-ref", "flake.nix", "flake.lock", ]; diff --git a/linux/server/Cargo.toml b/linux/server/Cargo.toml index 0eac9ab69..b802d06e5 100644 --- a/linux/server/Cargo.toml +++ b/linux/server/Cargo.toml @@ -1,6 +1,6 @@ # server -- the cider host-side daemon (Rust rewrite of ciderd, Stage 0 onward). # The lib crate is named "cider", so bins import it as cider::server, cider::registry, etc. -# See PLAN.md (plan) + PLAN.md (the make-or-break +# See docs/changelog.md (plan) + docs/changelog.md (the make-or-break # spike). Stage 0: bindgen the real xnu_sys hooks contract + link the prebuilt # xnu-sys static lib and call the real xnu_sys_init(&hooks). The prior end-to-end # link-model proof (stub xnu-sys) was validated in a throwaway prototype, since removed. @@ -18,7 +18,7 @@ name = "cider" name = "xnu_sys-link-proof" path = "src/main.rs" -# Stage 3 make-or-break spike (PLAN.md): a stackful Rust +# Stage 3 make-or-break spike (docs/changelog.md): a stackful Rust # microthread suspends/resumes across the xnu_sys FFI via a semaphore. [[bin]] name = "stage3-spike" diff --git a/linux/server/src/bin/blocking_msg_demo.rs b/linux/server/src/bin/blocking_msg_demo.rs index 7ed993d96..cb4d63652 100644 --- a/linux/server/src/bin/blocking_msg_demo.rs +++ b/linux/server/src/bin/blocking_msg_demo.rs @@ -9,7 +9,7 @@ //! Because the receiver's stack is discarded when it blocks, its receive buffer lives //! on the heap (stable address) and its result comes back through the syscall_return //! hook, not a Rust return. The guest task carries the demo's own pid so the message -//! copies target our own memory. See PLAN.md (bucket A + B.2). +//! copies target our own memory. See docs/changelog.md (bucket A + B.2). use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/bin/ciderd.rs b/linux/server/src/bin/ciderd.rs index 59e9f8417..544d0db0a 100644 --- a/linux/server/src/bin/ciderd.rs +++ b/linux/server/src/bin/ciderd.rs @@ -8,7 +8,7 @@ //! relocatable paths: DSERVER_LIBEXEC_PATH, DSERVER_MLDR_PATH, and the init selector //! (DSERVER_INIT / DARLING_NO_LAUNCHD). NOT runnable in the nix sandbox (needs root + //! CAP_SYS_ADMIN); validated by splicing into a cider runtime. See -//! PLAN.md (bucket B, the container main). +//! docs/changelog.md (bucket B, the container main). use cider::container::{self, Config}; use cider::handler::Handler; diff --git a/linux/server/src/bin/daemon_alloc_demo.rs b/linux/server/src/bin/daemon_alloc_demo.rs index 8dccef0ac..b57fa865f 100644 --- a/linux/server/src/bin/daemon_alloc_demo.rs +++ b/linux/server/src/bin/daemon_alloc_demo.rs @@ -5,7 +5,7 @@ //! process_vm_writev(client_pid, ...) via the write_memory hook -- exactly what the real //! daemon does. The client provides its own buffer address; the daemon's task for the //! client carries the client's pid, so the copyout lands in the client. See -//! PLAN.md (handler breadth over the socket). +//! docs/changelog.md (handler breadth over the socket). use cider::handler::Handler; use cider::mach; diff --git a/linux/server/src/bin/daemon_mach_demo.rs b/linux/server/src/bin/daemon_mach_demo.rs index a06c83bc4..12ba8142b 100644 --- a/linux/server/src/bin/daemon_mach_demo.rs +++ b/linux/server/src/bin/daemon_mach_demo.rs @@ -5,7 +5,7 @@ //! //! Uses a SEQPACKET socketpair (a genuine connected unix socket, but no filesystem path, //! so it works under the nix build sandbox) with the client in a forked child. See -//! PLAN.md (real-socket serving). +//! docs/changelog.md (real-socket serving). use cider::handler::Handler; use cider::registry::Registry; diff --git a/linux/server/src/bin/daemon_msg_demo.rs b/linux/server/src/bin/daemon_msg_demo.rs index fe5b3bf37..29a92e9bf 100644 --- a/linux/server/src/bin/daemon_msg_demo.rs +++ b/linux/server/src/bin/daemon_msg_demo.rs @@ -5,7 +5,7 @@ //! through XNU's ipc_mqueue, and copies the received message OUT to the client //! (copyoutmsg -> write_memory -> process_vm_writev(client)). Both copies cross the //! process boundary, exactly as the real daemon does. The round trip is proven by the -//! message id surviving. See PLAN.md (handler breadth over the socket). +//! message id surviving. See docs/changelog.md (handler breadth over the socket). use cider::handler::Handler; use cider::mach; diff --git a/linux/server/src/bin/daemon_session_demo.rs b/linux/server/src/bin/daemon_session_demo.rs index b34bf11cb..14fc69f85 100644 --- a/linux/server/src/bin/daemon_session_demo.rs +++ b/linux/server/src/bin/daemon_session_demo.rs @@ -5,7 +5,7 @@ //! microthread bound to the client's task (created once, parked between calls and woken //! by the socket loop), which dispatches each through the shared Handler and posts the //! reply. This composes checkin/checkout + the doWork loop + real-socket serving + -//! cross-process memory + the mach engine into one session. See PLAN.md. +//! cross-process memory + the mach engine into one session. See docs/changelog.md. use cider::handler::Handler; use cider::mach; diff --git a/linux/server/src/bin/mach_msg_demo.rs b/linux/server/src/bin/mach_msg_demo.rs index 1a20a1470..00f040fb9 100644 --- a/linux/server/src/bin/mach_msg_demo.rs +++ b/linux/server/src/bin/mach_msg_demo.rs @@ -7,7 +7,7 @@ //! (copyinmsg -> read_memory hook), XNU routes it through the port's ipc_mqueue, and //! the receive copies it OUT to the guest buffer (copyoutmsg -> write_memory hook). The //! guest task carries the demo's own pid, so both copies target our own memory. The -//! round trip is proven by the message id surviving. See PLAN.md. +//! round trip is proven by the message id surviving. See docs/changelog.md. use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/bin/mach_port_demo.rs b/linux/server/src/bin/mach_port_demo.rs index 40d0de012..9c645115d 100644 --- a/linux/server/src/bin/mach_port_demo.rs +++ b/linux/server/src/bin/mach_port_demo.rs @@ -4,7 +4,7 @@ //! composes the port machinery with the task_write_memory hook: the XNU trap's copyout //! runs copyoutmap -> xnu_sys_hooks->task_write_memory -> process_vm_writev (memory.c). //! The demo's guest task carries the demo's OWN pid, so that copyout lands in a local. -//! See PLAN.md (bucket A, mach IPC core). +//! See docs/changelog.md (bucket A, mach IPC core). use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/bin/mach_port_lifecycle_demo.rs b/linux/server/src/bin/mach_port_lifecycle_demo.rs index 9e2f920fc..c46fee009 100644 --- a/linux/server/src/bin/mach_port_lifecycle_demo.rs +++ b/linux/server/src/bin/mach_port_lifecycle_demo.rs @@ -3,7 +3,7 @@ //! copied out via the write_memory hook), destroy it with mach_port_mod_refs(-1), then //! confirm the name is gone (mach_port_type now returns KERN_INVALID_NAME). The guest //! task carries the demo's own pid so the type copyout lands in a local. See -//! PLAN.md (bucket A, mach IPC core). +//! docs/changelog.md (bucket A, mach IPC core). use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/bin/mach_traps_demo.rs b/linux/server/src/bin/mach_traps_demo.rs index d238be452..45da7f5db 100644 --- a/linux/server/src/bin/mach_traps_demo.rs +++ b/linux/server/src/bin/mach_traps_demo.rs @@ -4,7 +4,7 @@ //! valid Mach port name -- proving the daemon can serve Mach operations, the first //! step toward mach_msg. task_self_trap is ALSO driven through the generated //! dispatch() so the full RPC path (decode -> handler -> encode) is exercised end to -//! end. See PLAN.md (bucket A, mach IPC core). +//! end. See docs/changelog.md (bucket A, mach IPC core). use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/bin/mem_hooks_demo.rs b/linux/server/src/bin/mem_hooks_demo.rs index 8220d8666..fbee2c41f 100644 --- a/linux/server/src/bin/mem_hooks_demo.rs +++ b/linux/server/src/bin/mem_hooks_demo.rs @@ -1,7 +1,7 @@ //! Proof of the guest-memory hooks (task_read_memory / task_write_memory): the daemon //! reading AND writing another process's address space via process_vm_readv/writev -- //! the exact primitive the C++ daemon uses (Process::readMemory, process.cpp) and the -//! foundation mach_msg copyin/copyout depends on. See PLAN.md +//! foundation mach_msg copyin/copyout depends on. See docs/changelog.md //! (bucket B.1, the head of the critical path). //! //! Fork a child holding a known buffer at a known address; the parent reads it back diff --git a/linux/server/src/bin/persistent_threads_demo.rs b/linux/server/src/bin/persistent_threads_demo.rs index 1ac79f4d5..96c23bc5a 100644 --- a/linux/server/src/bin/persistent_threads_demo.rs +++ b/linux/server/src/bin/persistent_threads_demo.rs @@ -8,7 +8,7 @@ //! Proof: two guest threads each issue a "receive" that blocks (both park); delivering //! to one wakes exactly that thread (the other stays parked, untouched), and its reply //! combines a STACK-LOCAL stamped before the block with the delivered payload -- so the -//! stack survived. See PLAN.md. +//! stack survived. See docs/changelog.md. use cider::registry::Registry; use cider::sched; diff --git a/linux/server/src/bin/thread_call_loop_demo.rs b/linux/server/src/bin/thread_call_loop_demo.rs index d926ecb50..88f3a40cc 100644 --- a/linux/server/src/bin/thread_call_loop_demo.rs +++ b/linux/server/src/bin/thread_call_loop_demo.rs @@ -4,7 +4,7 @@ //! fresh microthread per call. Composes persistent threads (park/wake) with the //! generated dispatch: the loop waits for a call, dispatches it, posts the reply, then //! parks for the next; a per-thread stack counter proves the SAME thread served them -//! all. See PLAN.md (bucket B.2). +//! all. See docs/changelog.md (bucket B.2). use cider::mach; use cider::registry::Registry; diff --git a/linux/server/src/container.rs b/linux/server/src/container.rs index 0231bfc9f..eede358be 100644 --- a/linux/server/src/container.rs +++ b/linux/server/src/container.rs @@ -8,11 +8,11 @@ //! mapped-root inside a user namespace) plus CAP_SYS_ADMIN for unshare/mount/clone. It //! CANNOT run in the nix build sandbox and is not exercised by a flake-check demo; it is //! validated by splicing the daemon into a real cider runtime (the run recipe in -//! PLAN.md) and launching it through the cider launcher. Deferred vs the C++ +//! docs/changelog.md) and launching it through the cider launcher. Deferred vs the C++ //! for now (all best-effort prefix work, orthogonal to the namespace/mount/clone core): //! setupUserHome, ciderPreInit, fixPermissions, and rlimit bumps. The writable-/nix //! overlay (for guest Nix / M1) IS ported (`mount_nix_overlay`). See -//! PLAN.md (bucket B, the container main). +//! docs/changelog.md (bucket B, the container main). use std::ffi::CString; use std::io; diff --git a/linux/server/src/handler.rs b/linux/server/src/handler.rs index 0c838a77d..cf9d1380f 100644 --- a/linux/server/src/handler.rs +++ b/linux/server/src/handler.rs @@ -1,7 +1,7 @@ //! The daemon's reusable RPC handler: the real handler bodies, run on a microthread //! bound to the calling guest's task (so `sched::current_task()` and the `mach::*` //! traps act on that guest). Starts with the special-port Mach traps; this is where the -//! remaining ~70 calls get implemented as the daemon grows. See PLAN.md. +//! remaining ~70 calls get implemented as the daemon grows. See docs/changelog.md. use crate::bindings::xnu_sys_semaphore_t; use crate::rpc_wire::{self, *}; @@ -598,7 +598,7 @@ impl rpc_wire::RpcHandler for Handler { // rcv_name flags a port whose expected message never arrives (a Mach-side livelock) -- // a lead worth checking when a guest hangs. MACH_RCV_TIMED_OUT == 0x10004003. (Note: // the M1 config.status hang is NOT this -- it is a guest bash here-doc pipe deadlock - // with the daemon idle; see PLAN.md "M1 status 2026-07-27".) + // with the daemon idle; see docs/changelog.md "M1 status 2026-07-27".) if code as u32 == 0x10004003 && std::env::var_os("DSERVER_TRACE_MSG").is_some() { eprintln!( "ciderd: mach_msg RCV TIMED_OUT rcv_name={} option={:#x} timeout={}", diff --git a/linux/server/src/kqchan.rs b/linux/server/src/kqchan.rs index 5056633de..ddae7947e 100644 --- a/linux/server/src/kqchan.rs +++ b/linux/server/src/kqchan.rs @@ -5,7 +5,7 @@ //! latest event), and the daemon pushes a `notification` message when a watched event //! (e.g. the target's death -> NOTE_EXIT) occurs. Raw SEQPACKET framing (one struct per //! message, no length prefix), matching ciderd's MessageQueue. Mirrors -//! DarlingServer::Kqchan::Process (kqchan.cpp). See PLAN.md (bucket B.8). +//! DarlingServer::Kqchan::Process (kqchan.cpp). See docs/changelog.md (bucket B.8). use crate::bindings::xnu_sys_task_t; use crate::sched; diff --git a/linux/server/src/lib.rs b/linux/server/src/lib.rs index f0297256f..9a86dac0c 100644 --- a/linux/server/src/lib.rs +++ b/linux/server/src/lib.rs @@ -2,7 +2,7 @@ //! //! Boundary (frozen): the C xnu-sys (XNU emulation) is consumed via the xnu_sys_* //! API + the xnu_sys_hooks vtable; everything above it is safe-ish Rust. See -//! PLAN.md. Proven so far (spikes): the link + xnu_sys_init +//! docs/changelog.md. Proven so far (spikes): the link + xnu_sys_init //! (Stage 0), both microthread suspend/resume paths across the FFI (Stage 3), and //! the byte-identical RPC wire codec (Stage 1). This library is the Stage 4 //! foundation: a reusable, static-mut-free microthread scheduler + hook layer diff --git a/linux/server/src/mach.rs b/linux/server/src/mach.rs index 10056fbf3..abad3062e 100644 --- a/linux/server/src/mach.rs +++ b/linux/server/src/mach.rs @@ -5,7 +5,7 @@ //! a microthread bound to the guest's task via the registry. These are the first real //! Mach calls on the way to mach_msg (they take no message, so they need no copyin/ //! copyout and never block). Mirrors call.cpp's TaskSelfTrap/... handlers. See -//! PLAN.md (bucket A, mach IPC core). +//! docs/changelog.md (bucket A, mach IPC core). // Declared here in an `extern "C"` block until xnu-sys became Rust (#71); imported directly // now (#75), because the linker matched those by name and rustc never compared them against diff --git a/linux/server/src/psynch.rs b/linux/server/src/psynch.rs index 104067fc4..b740184eb 100644 --- a/linux/server/src/psynch.rs +++ b/linux/server/src/psynch.rs @@ -15,7 +15,7 @@ //! Every op returns an `int` errno (0 == success) and writes the u32 retval through a //! pointer -- the same `retvalPointer` (a stable per-thread slot) the C++ //! `Call::::processCall` passes; see the handler wiring in `handler.rs`. See -//! PLAN.md (the psynch bucket). +//! docs/changelog.md (the psynch bucket). // Declared here in an `extern "C"` block until xnu-sys became Rust (#71); imported directly // now (#75). All nine are the thin forwarding wrappers in xnu_sys_psynch, which supply diff --git a/linux/server/src/registry.rs b/linux/server/src/registry.rs index 3d57c6431..2d5eb4f0f 100644 --- a/linux/server/src/registry.rs +++ b/linux/server/src/registry.rs @@ -2,7 +2,7 @@ //! run on its OWN task (its own XNU state), not the shared kernel task. A microthread //! spawned via `spawn_on` is bound to the guest's task, so sched::current_task() //! inside its handler returns that task -- the routing mechanism. See -//! PLAN.md (Stage 4). +//! docs/changelog.md (Stage 4). use crate::bindings::*; use crate::sched::{self, Microthread, TaskCtx}; diff --git a/linux/server/src/rpc_io.rs b/linux/server/src/rpc_io.rs index 367fb573a..a1df3c194 100644 --- a/linux/server/src/rpc_io.rs +++ b/linux/server/src/rpc_io.rs @@ -1,7 +1,7 @@ //! Daemon-side RPC message I/O: the receive/decode half of the loop. recvmsg a //! message (with SCM_RIGHTS fds) off the unix socket, decode its callhdr, and //! dispatch by call number via the generated codec. Pure socket/wire code -- no -//! xnu-sys. See PLAN.md (Stage 4). +//! xnu-sys. See docs/changelog.md (Stage 4). use crate::rpc_wire::{callnum_name, DserverRpcCallhdr}; use std::io; diff --git a/linux/server/src/server.rs b/linux/server/src/server.rs index 1be079db3..5a949e65d 100644 --- a/linux/server/src/server.rs +++ b/linux/server/src/server.rs @@ -2,7 +2,7 @@ //! every guest sends its RPC datagrams, multiplexed through one epoll into the receive -> //! dispatch -> reply cycle. Connectionless: replies go back to each sender's address. //! This matches the real guest transport (server.cpp:452); call handlers plug in via the -//! closure passed to `run`. See PLAN.md. +//! closure passed to `run`. See docs/changelog.md. use crate::rpc_io::{recv_datagram, send_datagram, Message}; use std::ffi::CString; diff --git a/linux/server/src/task.rs b/linux/server/src/task.rs index 3bb516d87..e5cca8dd6 100644 --- a/linux/server/src/task.rs +++ b/linux/server/src/task.rs @@ -2,7 +2,7 @@ //! take an explicit `xnu_sys_task_t*` (unlike the mach traps, which act on the current //! task). A handler gets the task pointer from `sched::current_task()` -- the task its //! microthread is bound to -- and passes it here. Mirrors the `process->_dtapeTask` -//! calls in call.cpp. See PLAN.md (bucket A). +//! calls in call.cpp. See docs/changelog.md (bucket A). use crate::bindings::{xnu_sys_semaphore_t, xnu_sys_task_t}; diff --git a/linux/server/src/thread.rs b/linux/server/src/thread.rs index fea1b205e..0b8bb99be 100644 --- a/linux/server/src/thread.rs +++ b/linux/server/src/thread.rs @@ -3,7 +3,7 @@ //! guest thread, it brackets the interrupt with interrupt_enter/interrupt_exit, and the //! daemon tells XNU the thread has entered/left its sigexc state. Mirrors the //! xnu_sys_thread_sigexc_* calls in call.cpp's InterruptEnter/InterruptExit + thread.cpp. -//! See PLAN.md (bucket B.4). +//! See docs/changelog.md (bucket B.4). use crate::bindings::xnu_sys_thread_t; diff --git a/linux/server/src/traps.rs b/linux/server/src/traps.rs index c76faaaf7..af41823b3 100644 --- a/linux/server/src/traps.rs +++ b/linux/server/src/traps.rs @@ -5,7 +5,7 @@ //! are GUEST addresses (the trap copies results out via the write_memory hook). The //! traps act on the CURRENT task/thread (per the running microthread), so a handler //! calling them must run on a microthread bound to the guest's task via the registry. -//! Mirrors generate-rpc-wrappers.py's DSERVER_XNU_SYS_DECLS. See PLAN.md. +//! Mirrors generate-rpc-wrappers.py's DSERVER_XNU_SYS_DECLS. See docs/changelog.md. use crate::bindings::xnu_sys_task_t; diff --git a/nix/ciderBuilderModule.nix b/nix/ciderBuilderModule.nix index 8805bdd4b..aa023a44f 100644 --- a/nix/ciderBuilderModule.nix +++ b/nix/ciderBuilderModule.nix @@ -26,7 +26,7 @@ # # nix build nixpkgs#hello --system x86_64-darwin # -# See: PLAN.md +# See: docs/changelog.md { config, lib, diff --git a/nix/lib/cider-src.nix b/nix/lib/cider-src.nix index 69f3ad0de..83ce3714b 100644 --- a/nix/lib/cider-src.nix +++ b/nix/lib/cider-src.nix @@ -78,7 +78,7 @@ let "flake.nix" "flake.lock" "docs" - "PLAN.md" + "docs/changelog.md" "README.md" "CONTRIBUTORS.md" ".git" diff --git a/nix/lib/ciderBuck2Graph.nix b/nix/lib/ciderBuck2Graph.nix index b501702f3..73a31e2b7 100644 --- a/nix/lib/ciderBuck2Graph.nix +++ b/nix/lib/ciderBuck2Graph.nix @@ -123,10 +123,10 @@ "plan" "docs" "nix" - # The prose. A PLAN.md edit cost a 30 minute graph rebuild and a full relowering + # The prose. A docs/changelog.md edit cost a 30 minute graph rebuild and a full relowering # before this line: the lowering had already been taught to ignore it # (d8af37a70), the graph had not. - "PLAN.md" + "docs/changelog.md" "README.md" "CONTRIBUTORS.md" # The generators and the check suite. buck2 never opens one: the only path diff --git a/nix/lib/ciderBuck2Lower.nix b/nix/lib/ciderBuck2Lower.nix index 854bec682..67c911aa5 100644 --- a/nix/lib/ciderBuck2Lower.nix +++ b/nix/lib/ciderBuck2Lower.nix @@ -75,13 +75,13 @@ # Without this, editing any generator relowers all 259 derivations, and this port # is largely a matter of editing generators. "scripts" - # Documentation and editor/tool state. PLAN.md is the one that matters: it is + # Documentation and editor/tool state. docs/changelog.md is the one that matters: it is # 137K, it is edited in essentially every increment of this port, and until now # every one of those edits relowered all 259 derivations. Nothing reads any of - # these -- the only mentions of PLAN.md across every BUCK and .bzl in the tree + # these -- the only mentions of docs/changelog.md across every BUCK and .bzl in the tree # are comments pointing a reader at it, there is no BUCK package at the repo # root, and a buck2 glob cannot escape its own package. - "PLAN.md" + "docs/changelog.md" "README.md" "CONTRIBUTORS.md" "LICENSE" @@ -844,7 +844,7 @@ # path from 306,019 files to about 131,048 and left it SHARED, so it cut no cascade. It was # measured inert for the shipping endpoint, identical drvPath with the flag either way, # because prefix-min sets sourceGroups and never reads this. The evidence it did produce is - # kept in PLAN.md rather than here. + # kept in docs/changelog.md rather than here. projectSrc = src; # ---- per-component source groups (#54) ---------------------------------- diff --git a/pins/ciderd/VENDORED.md b/pins/ciderd/VENDORED.md index 88c3ad439..ebd3af53a 100644 --- a/pins/ciderd/VENDORED.md +++ b/pins/ciderd/VENDORED.md @@ -13,7 +13,7 @@ sources can be edited in-tree without the patch-file indirection. (opt-in via a `.enable-writable-nix` marker in the prefix). Was `patches/ciderd/0001-optional-writable-nix-overlay.patch`. - `src/message.cpp` — cache the daemon's own `ucred` (pid/uid/gid) instead of - calling getpid/getuid/getgid per message; see `PLAN.md`. Was + calling getpid/getuid/getgid per message; see `docs/changelog.md`. Was `patches/ciderd/0002-cache-ciderd-own-credentials.patch`. ## Re-syncing with upstream diff --git a/pins/ciderd/scripts/generate-rpc-wrappers.py b/pins/ciderd/scripts/generate-rpc-wrappers.py index 8ee34939e..48fa57ffe 100755 --- a/pins/ciderd/scripts/generate-rpc-wrappers.py +++ b/pins/ciderd/scripts/generate-rpc-wrappers.py @@ -1628,7 +1628,7 @@ library_source.close() # Optional 5th arg: emit byte-identical Rust structs for the RPC wire messages # from the SAME `calls` list (single source of truth). x86_64 layout: Rust # #[repr(C)] already 8-aligns u64/i64, matching the C __attribute__((aligned(8))). -# The C outputs above are untouched. See PLAN.md (Stage 1). +# The C outputs above are untouched. See docs/changelog.md (Stage 1). if len(sys.argv) > 5: RUST_WIRE_MAP = { 'bool': 'bool', diff --git a/scripts/buck-endpoint-stale.nu b/scripts/buck-endpoint-stale.nu index a8d6eff30..aca235ded 100755 --- a/scripts/buck-endpoint-stale.nu +++ b/scripts/buck-endpoint-stale.nu @@ -42,7 +42,7 @@ # reads. Editing tests/cider-buck2-smoke.nix was reported stale and the prefix derivation # did not move at all. const NEUTRAL_TOPS = [ - "plan" "docs" "nix" "scripts" "PLAN.md" "README.md" "CONTRIBUTORS.md" + "plan" "docs" "nix" "scripts" "docs/changelog.md" "README.md" "CONTRIBUTORS.md" ".git" ".jj" ".direnv" "buck-out" "flake.nix" "flake.lock" "result-graph-ref" ] diff --git a/scripts/buck-env-names-check.nu b/scripts/buck-env-names-check.nu index d25bade0e..209203be5 100755 --- a/scripts/buck-env-names-check.nu +++ b/scripts/buck-env-names-check.nu @@ -96,7 +96,7 @@ const NAMESPACES = ["CIDER" "DARLING" "XTRACE"] # Where we talk to people or drive the build. darwin/ and linux/ are deliberately absent: # a C macro is not an advertisement, and those trees are where the macros live. const ADVERTISING_DIRS = ["scripts" "tests" "docs" "nix" "etc" "tools" "plan"] -const ADVERTISING_FILES = ["PLAN.md" "README.md" "flake.nix"] +const ADVERTISING_FILES = ["docs/changelog.md" "README.md" "flake.nix"] # Everything first-party, for the read side. Pins are excluded: they are upstream, they cannot # implement a name we invented, and walking them costs far more than the check is worth. diff --git a/scripts/buck-move-pins.nu b/scripts/buck-move-pins.nu index df549f848..0f3da37b1 100755 --- a/scripts/buck-move-pins.nu +++ b/scripts/buck-move-pins.nu @@ -29,7 +29,7 @@ # "# cmake target:" and "buck-registry:" the FROZEN REFERENCE path space # "previously the submodule" VENDORED.md sentences about the PAST # "/build/build/" the cmake BINARY DIR path space -# PLAN.md 34 hits mixing current layout with past failures; +# docs/changelog.md 34 hits mixing current layout with past failures; # only a reader can tell which is which # # PORTED FROM PYTHON (#98), byte identical in dry run over the whole tracked tree. @@ -53,7 +53,7 @@ const MANIFEST = "nix/submodules.json" # spelling in order to explain which lines are frozen and why, so a sweep that rewrote it would # destroy the explanation of what the sweep does. const SELF = "scripts/buck-move-pins.nu" -const SKIP_FILES = ["PLAN.md" "nix/submodules.json" "scripts/buck-move-pins.nu"] +const SKIP_FILES = ["docs/changelog.md" "nix/submodules.json" "scripts/buck-move-pins.nu"] const SKIP_REL_PREFIX = ["linux/server/target/"] # Lines that mention the string but must keep the old spelling. @@ -215,7 +215,7 @@ def main [--apply, --dry-run (-n)] { } if not $apply { say "" - say "DRY RUN. Nothing was written. PLAN.md and the manifest text are excluded; pass --apply to write." + say "DRY RUN. Nothing was written. docs/changelog.md and the manifest text are excluded; pass --apply to write." } exit 0 } diff --git a/scripts/buck-move-src-subdir.nu b/scripts/buck-move-src-subdir.nu index 4d89e952a..49404e8e6 100755 --- a/scripts/buck-move-src-subdir.nu +++ b/scripts/buck-move-src-subdir.nu @@ -101,7 +101,7 @@ def candidate-files [] { | where {|l| $l != "" } | each {|p| $p | str substring 2.. }) # IN os.walk ORDER, which is what the report's tie breaking depends on. find interleaves: it # descends into a subdirectory the moment it meets one, so scripts/x came out before the root - # PLAN.md. os.walk is top-down by DIRECTORY: every file of a directory, then its children. So + # docs/changelog.md. os.walk is top-down by DIRECTORY: every file of a directory, then its children. So # the directories are listed once, in the same depth-first order, and the files are bucketed # into them. Two files with one reference each swapped places without this, which is the only # way the report can differ once the counts agree. diff --git a/scripts/buck-quick-check.nu b/scripts/buck-quick-check.nu index 6aee6479c..dcbc58a47 100755 --- a/scripts/buck-quick-check.nu +++ b/scripts/buck-quick-check.nu @@ -44,7 +44,7 @@ # iterating on the lowering itself pays it every single time, and nothing else does. # # --probe is only quick for a path both source filters exclude (scripts/, nix/, docs/, -# PLAN.md), and probing those tests nothing about the cascade for the same reason they are +# docs/changelog.md), and probing those tests nothing about the cascade for the same reason they are # cheap. A probe anywhere else moves cider-src, which USED to mean ld64 (about 26 min) and # the graph (about 18 min) rebuilt before the target was reached. Both are gone now: #56 took # the project out of the graph inputs, and #65 replaced the external ld64 with the buck2 built @@ -123,7 +123,7 @@ def counter-selftest [] { } # What this probe will actually cost, which is not one number. Both source filters exclude -# scripts/, nix/, docs/ and PLAN.md, so a probe there re-evaluates and rebuilds NOTHING; +# scripts/, nix/, docs/ and docs/changelog.md, so a probe there re-evaluates and rebuilds NOTHING; # anything else moves cider-src. # # THIS COMMENT USED TO END "and drags ld64 and the graph in ahead of the target". Both halves @@ -131,7 +131,7 @@ def counter-selftest [] { # linker, and nix/cctools-port.nix, the file the claim pointed at, no longer exists. def probe-cost []: string -> string { let path = $in - let free = ["docs/", "PLAN.md"] + let free = ["docs/", "docs/changelog.md"] # scripts/ and nix/ are excluded from the SOURCE FILTERS, so they do not move cider-src, # but that is not the same as free: several are nix path INPUTS, referenced as # ${../../scripts/}, so editing one moves the derivation that uses it. Measured: diff --git a/scripts/buck-script-refs-check.nu b/scripts/buck-script-refs-check.nu index cc9e22cc4..4fedfc0ca 100755 --- a/scripts/buck-script-refs-check.nu +++ b/scripts/buck-script-refs-check.nu @@ -24,7 +24,7 @@ def say [msg: string] { print -e $msg } # per-pin BUCK files are generated from one template, so a stale name there is one fix, and # including them makes every run walk 49 files to say the same thing four dozen times. const DEFAULT_GLOBS = [ - "PLAN.md" + "docs/changelog.md" "flake.nix" ".gitignore" "plan/**/*.md" diff --git a/scripts/buck-test.nu b/scripts/buck-test.nu index 76d1f2110..9505b6b36 100755 --- a/scripts/buck-test.nu +++ b/scripts/buck-test.nu @@ -429,7 +429,7 @@ def main [flag?: string] { # THESE TWO EXIST, ARE CREDITED WITH SAVING AN HOUR EACH, AND NOTHING RAN THEM. Measured # 2026-08-11: neither buck-labels-check.nu nor buck-pin-paths-check.nu appeared anywhere in - # this file, and a whole-tree search found no other caller either, only prose in PLAN.md + # this file, and a whole-tree search found no other caller either, only prose in docs/changelog.md # and mentions inside other scripts. That is the exact shape #85 was opened for, a check # that exists and never runs, and it had quietly come back for two of them. # diff --git a/scripts/build-hello-under-cider.nu b/scripts/build-hello-under-cider.nu index ebeccc2bf..4fcbc3bfd 100755 --- a/scripts/build-hello-under-cider.nu +++ b/scripts/build-hello-under-cider.nu @@ -6,7 +6,7 @@ # fix (patches/xnu/0006) to get GNU hello's ./configure past its dup2 check. # # It runs the whole ./configure && make && ./hello in ONE `cider shell` -# session (rootless cannot re-join a container; see PLAN.md), staging +# session (rootless cannot re-join a container; see docs/changelog.md), staging # the bootstrap-tools, the apple-sdk and the hello tarball through Darling's # host-root mount (/Volumes/SystemRoot), building in the writable container # $HOME with a writable TMPDIR, and forcing CONFIG_SHELL to the bootstrap bash diff --git a/scripts/build-trivial.nu b/scripts/build-trivial.nu index 0af930c71..df163ac24 100755 --- a/scripts/build-trivial.nu +++ b/scripts/build-trivial.nu @@ -40,7 +40,7 @@ # flag argument is rejected by the nushell parser with exit 1 where bash printed its # own message and exited 2. # -# See: PLAN.md (Task 4.1) +# See: docs/changelog.md (Task 4.1) def colours [] { if (is-terminal --stdout) { @@ -148,7 +148,7 @@ def level1_echo_to_out [ctx: record] { print " \u{2022} 'Bad file descriptor' / ENOEXEC: sandbox-exec stub issue (Phase 2)" print " \u{2022} 'sandbox profile' write fail: check /tmp is writable inside prefix" print " \u{2022} Builder hangs: posix_spawn with POSIX_SPAWN_SETEXEC broken" - print " \u{2022} 'Unimplemented syscall': check PLAN.md" + print " \u{2022} 'Unimplemented syscall': check docs/changelog.md" print "" print " Manual reproduction:" print $" cider shell bash -lc 'nix-build -vvvv --no-out-link --expr \"($expr)\"'" @@ -596,7 +596,7 @@ def main [ log_ $c "Phase 4.1 is complete! Next steps:" log_ $c " \u{2022} Phase 4.2: Test bash in build sandboxes (more complex scripts)" log_ $c " \u{2022} Phase 4.5: Build a C program with Darwin stdenv" - log_ $c " \u{2022} See: PLAN.md" + log_ $c " \u{2022} See: docs/changelog.md" exit 0 } } diff --git a/scripts/cc-under-cider.sh b/scripts/cc-under-cider.sh index 5ac40fa90..aeaead2d1 100755 --- a/scripts/cc-under-cider.sh +++ b/scripts/cc-under-cider.sh @@ -8,7 +8,7 @@ # It stages the host source through Darling's host-root mount # (/Volumes/SystemRoot) and compiles into the container's writable $HOME # (/tmp is read-only inside; clang also needs a writable TMPDIR there). Runs a -# single fresh container (rootless cannot re-join one; see PLAN.md). +# single fresh container (rootless cannot re-join one; see docs/changelog.md). # # Prereqs (substitute once on the host): # nix copy --from https://cache.nixos.org \ diff --git a/scripts/cider-build-hook b/scripts/cider-build-hook index c3a859764..54eef6e54 100755 --- a/scripts/cider-build-hook +++ b/scripts/cider-build-hook @@ -37,7 +37,7 @@ # ./scripts/cider-build-hook --check # ./scripts/cider-build-hook --help # -# See: PLAN.md (Task 7.4) +# See: docs/changelog.md (Task 7.4) set -euo pipefail @@ -277,7 +277,7 @@ Examples: # "/path/to/cider-build-hook x86_64-darwin - 4 1 - - -" # ]; -See: PLAN.md (Task 7.4) +See: docs/changelog.md (Task 7.4) EOF } diff --git a/scripts/cider-nix b/scripts/cider-nix index f63d6750b..62dac75df 100755 --- a/scripts/cider-nix +++ b/scripts/cider-nix @@ -25,7 +25,7 @@ # DPREFIX Override the default Darling prefix (~/.cider) # CIDER_NIX_VERBOSE Set to 1 for debug output (DARLING_NIX_VERBOSE still honoured) # -# See: PLAN.md (Task 3.4) +# See: docs/changelog.md (Task 3.4) set -euo pipefail diff --git a/scripts/gen-prefix-min.nu b/scripts/gen-prefix-min.nu index c306c5396..5bf2b1c9a 100755 --- a/scripts/gen-prefix-min.nu +++ b/scripts/gen-prefix-min.nu @@ -32,7 +32,7 @@ const EXCLUDE_PKGS = [ "//buck-src/ruby" # zsh's 35 loadable modules. Removing the zsh BINARY by label left these installed at # usr/lib/zsh/5.7.1/zsh/*.so, orphaned: nothing can load them. This is also the omission - # PLAN.md recorded from the start, that the prose claimed the scripting languages were + # docs/changelog.md recorded from the start, that the prose claimed the scripting languages were # excluded while the list omitted //buck-src/zsh. Closed at the package level. "//buck-src/zsh" ] diff --git a/scripts/gnix-build.sh b/scripts/gnix-build.sh index ffb6d138a..2bbeb22e9 100755 --- a/scripts/gnix-build.sh +++ b/scripts/gnix-build.sh @@ -8,7 +8,7 @@ # GDB host path to the closure DB dump (reached via /Volumes/SystemRoot) # GBIN (optional) basename of a binary in the output's bin/ to run --version # See scripts/gnix-hello.sh for the hello-specific M1 driver and -# PLAN.md for the launchd-bypass background. +# docs/changelog.md for the launchd-bypass background. # # STAYS BASH. This runs inside the GUEST, under a cider shell session, where the # shell is Darwin bash 3.2.57 and there is no nushell in the prefix. The bash-to- @@ -44,7 +44,7 @@ echo "=BUILD $GDRV=" # ^* builds all outputs, so multi-output packages (bin/lib/dev/...) work too. # Retry: guest test/build binaries occasionally crash with a transient signal # (e.g. SIGFPE in an autoconf mbrtowc/locale probe) -- a cider execution- -# fidelity flake, not a real build error (see PLAN.md, task #44). +# fidelity flake, not a real build error (see docs/changelog.md, task #44). # nix builds are atomic, so a fresh attempt re-runs configure and usually passes. brc=1 for attempt in 1 2 3 4; do diff --git a/scripts/gnix-hello.sh b/scripts/gnix-hello.sh index 20170b16f..7b1c1b6e7 100755 --- a/scripts/gnix-hello.sh +++ b/scripts/gnix-hello.sh @@ -2,9 +2,9 @@ # gnix-hello.sh -- runs INSIDE one cider shell session (rootless one-shot). # Guest `nix build` compiles GNU hello FROM SOURCE via the darwin stdenv and runs # it: the *official* campaign M1 (vs the toolchain M1 in build-hello-under-cider.nu). -# See PLAN.md. Everything below is solved; the build currently reaches +# See docs/changelog.md. Everything below is solved; the build currently reaches # hello's configure and trips the ciderd fork/exec concurrency bug at the -# first clang call (PLAN.md). +# first clang call (docs/changelog.md). # # Requires (set up on the HOST before `cider shell sh `): # - /.enable-writable-nix (ciderd overlays a writable native /nix) @@ -58,7 +58,7 @@ echo "=BUILD=" [ -e "$HELLO_DRV" ] || { echo "NO_HELLO_DRV"; exit 1; } # Retry: guest build/test binaries occasionally take a transient signal (e.g. # SIGFPE in an autoconf mbrtowc/locale probe) -- a cider execution-fidelity -# flake, not a real build error (PLAN.md, task #44). nix builds are +# flake, not a real build error (docs/changelog.md, task #44). nix builds are # atomic, so a fresh attempt re-runs configure and usually passes. brc=1 for attempt in 1 2 3 4; do diff --git a/scripts/install-nix-in-cider.nu b/scripts/install-nix-in-cider.nu index 997a92115..f12c99547 100755 --- a/scripts/install-nix-in-cider.nu +++ b/scripts/install-nix-in-cider.nu @@ -380,7 +380,7 @@ fi say $c $"Verification: ($c.green)($pass) passed($c.reset), ($c.red)($fail) failed($c.reset)" if $fail > 0 { warn $c "Some checks failed. Nix may be partially functional." - warn $c "See PLAN.md for debugging tips." + warn $c "See docs/changelog.md for debugging tips." } } else { say $c $"($c.bold)Step 7: Skipping verification($c.reset) \(--no-verify)" diff --git a/scripts/run-darwin-under-cider.sh b/scripts/run-darwin-under-cider.sh index a784f0554..9251664d7 100755 --- a/scripts/run-darwin-under-cider.sh +++ b/scripts/run-darwin-under-cider.sh @@ -10,7 +10,7 @@ # # Each `cider shell` runs in its own fresh container: rootless can create a # container but cannot join an existing one from a sibling user namespace (see -# PLAN.md), and a fresh prefix's first boot races the shellspawn +# docs/changelog.md), and a fresh prefix's first boot races the shellspawn # socket, so we kill any stale ciderd and retry. # # Usage: diff --git a/scripts/triage-syscalls.nu b/scripts/triage-syscalls.nu index 6bf8295ed..cf79d6723 100755 --- a/scripts/triage-syscalls.nu +++ b/scripts/triage-syscalls.nu @@ -4,7 +4,7 @@ # This script runs various Nix operations inside a Darling prefix and # captures "Unimplemented syscall" messages, ENOSYS errors, and other # indicators of missing kernel functionality. The output is a table -# suitable for pasting into PLAN.md. +# suitable for pasting into docs/changelog.md. # # Usage: # ./scripts/triage-syscalls.nu [OPTIONS] @@ -46,7 +46,7 @@ # header block, and an unknown option is rejected by the nushell parser with # exit 1 where bash printed its own message and exited 1 as well. # -# See: PLAN.md (Task 1.7) +# See: docs/changelog.md (Task 1.7) # Known syscall number to name mapping (macOS/XNU BSD syscalls) # Source: pins/xnu/bsd/kern/syscalls.master @@ -623,7 +623,7 @@ def main [ let name = ($f | get 1) if not ($num in $seen) { $seen = ($seen | append $num) - $rep = ($rep | append $"- **Syscall ($num)** \(`($name)`\): Add to syscall triage table in `PLAN.md`") + $rep = ($rep | append $"- **Syscall ($num)** \(`($name)`\): Add to syscall triage table in `docs/changelog.md`") } } $rep = ($rep | append "") @@ -632,7 +632,7 @@ def main [ $rep = ($rep | append [ "### Next Steps" "" - "1. Add any new syscalls to `PLAN.md`" + "1. Add any new syscalls to `docs/changelog.md`" '2. For each "Must fix" syscall, determine the best implementation strategy:' " - Full translation to Linux equivalent" " - Stub returning ENOTSUP (if caller handles gracefully)" @@ -665,7 +665,7 @@ def main [ $rep = ($rep | append [ "" "---" - "*Generated by `scripts/triage-syscalls.nu` \u{2014} see [PLAN.md](../PLAN.md)*" + "*Generated by `scripts/triage-syscalls.nu` \u{2014} see [docs/changelog.md](../docs/changelog.md)*" ]) # ── Output ────────────────────────────────────────────────────────────── diff --git a/scripts/verify-nix.nu b/scripts/verify-nix.nu index 3f32aebd0..77cf1e6c2 100755 --- a/scripts/verify-nix.nu +++ b/scripts/verify-nix.nu @@ -337,7 +337,7 @@ def main [ print -e "" print -e "Troubleshooting:" print -e " \u{2022} If Nix binaries aren't found, run: ./scripts/install-nix-in-cider.nu" - print -e " \u{2022} If syscall warnings appear, check: PLAN.md" + print -e " \u{2022} If syscall warnings appear, check: docs/changelog.md" print -e " \u{2022} If evaluator fails, try: cider shell bash -lc 'nix eval --expr 1+1' 2>&1" print -e " \u{2022} For detailed tracing: env DYLD_INSERT_LIBRARIES=/usr/lib/cider/libxtrace.dylib cider shell bash -lc 'nix --version'" print -e " \u{2022} For host-side tracing: strace -f -p $(pidof ciderd) 2>&1 | head -500" diff --git a/scripts/with-watchdog.sh b/scripts/with-watchdog.sh index 46792df55..d5823bd31 100755 --- a/scripts/with-watchdog.sh +++ b/scripts/with-watchdog.sh @@ -7,7 +7,7 @@ # a Mach IPC wait that never wakes. When a build or test hangs, a plain timeout # tells us nothing; this wrapper attaches gdb to every relevant process on # timeout and dumps backtraces so the stall can be triaged -# (see PLAN.md). +# (see docs/changelog.md). # # Usage: # scripts/with-watchdog.sh [--timeout SECONDS] [--label NAME] diff --git a/templates/cider-builder/README.md b/templates/cider-builder/README.md index 550a7f1c1..d912f2615 100644 --- a/templates/cider-builder/README.md +++ b/templates/cider-builder/README.md @@ -131,7 +131,7 @@ ssh -vvv -i /etc/nix/cider-builder-key -p 2222 root@127.0.0.1 echo ok The derivation uses a macOS syscall that Darling doesn't support yet. This is expected for complex packages. Check the -[syscall triage table](https://github.com/nixie-dev/cider-nix/blob/main/PLAN.md) +[syscall triage table](https://github.com/nixie-dev/cider-nix/blob/main/docs/changelog.md) and consider filing an issue upstream. ### Build is very slow @@ -146,6 +146,6 @@ and consider filing an issue upstream. ## Further Reading - [Full setup guide](https://github.com/nixie-dev/cider-nix/blob/main/docs/darwin-builder.md) -- [Project plan](https://github.com/nixie-dev/cider-nix/blob/main/PLAN.md) +- [Project plan](https://github.com/nixie-dev/cider-nix/blob/main/docs/changelog.md) - [Darling documentation](https://docs.darlinghq.org/) - [Nix distributed builds](https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html) \ No newline at end of file diff --git a/tests/buck2/guest/sec_probe.c b/tests/buck2/guest/sec_probe.c index 5d514e0f5..a14582cd0 100644 --- a/tests/buck2/guest/sec_probe.c +++ b/tests/buck2/guest/sec_probe.c @@ -2,7 +2,7 @@ // // Security is the largest cone the port builds that had never executed: 38 static archives // and 5 dylibs, and a 9.3MB framework binary. Its exported-symbols list is what pulls the -// archive members in at link time (see the security_exp note in PLAN.md), so "it links" and +// archive members in at link time (see the security_exp note in docs/changelog.md), so "it links" and // "its code runs" are further apart here than anywhere else in the tree. // // Everything is self-contained: a known digest, and a certificate embedded below as DER so diff --git a/tests/cider-builder.nix b/tests/cider-builder.nix index b532d1b72..d17c8ec08 100644 --- a/tests/cider-builder.nix +++ b/tests/cider-builder.nix @@ -13,7 +13,7 @@ # Usage: # nix build .#checks.x86_64-linux.cider-builder -L # -# See: PLAN.md +# See: docs/changelog.md { pkgs, cider, ciderBuilderModule, ... }: let diff --git a/tests/cider-smoke.nix b/tests/cider-smoke.nix index 26daf7c44..18ca1a3a8 100644 --- a/tests/cider-smoke.nix +++ b/tests/cider-smoke.nix @@ -8,7 +8,7 @@ # Usage: # nix build .#checks.x86_64-linux.cider-smoke -L # -# See: PLAN.md (Task 6.6) +# See: docs/changelog.md (Task 6.6) { pkgs, cider, ... }: let diff --git a/tests/dirserv/test_dirserv.sh b/tests/dirserv/test_dirserv.sh index a7b08c266..f0dd98bea 100644 --- a/tests/dirserv/test_dirserv.sh +++ b/tests/dirserv/test_dirserv.sh @@ -12,7 +12,7 @@ # 0 — all tests passed # 1 — one or more tests failed # -# See: PLAN.md (Task 5.1) +# See: docs/changelog.md (Task 5.1) set -eu diff --git a/tests/nix-in-cider.nix b/tests/nix-in-cider.nix index 451c95fd2..5193dc787 100644 --- a/tests/nix-in-cider.nix +++ b/tests/nix-in-cider.nix @@ -15,7 +15,7 @@ # Usage: # nix build .#checks.x86_64-linux.nix-in-cider -L # -# See: PLAN.md (Task 6.1) +# See: docs/changelog.md (Task 6.1) { pkgs, cider, ... }: let diff --git a/tests/nix/compatibility-matrix.sh b/tests/nix/compatibility-matrix.sh index 7bf5485e3..dd58cddc7 100755 --- a/tests/nix/compatibility-matrix.sh +++ b/tests/nix/compatibility-matrix.sh @@ -29,7 +29,7 @@ # The script is designed to be run periodically (e.g., in CI) and its JSON # output can be compared across runs to detect regressions and progress. # -# See: PLAN.md (Tasks 6.5, 7.6) +# See: docs/changelog.md (Tasks 6.5, 7.6) set -euo pipefail @@ -398,7 +398,7 @@ Examples: # CI mode: JSON only, fail on regressions ./tests/nix/compatibility-matrix.sh --json --tier 1,2 -See: PLAN.md (Task 6.5) +See: docs/changelog.md (Task 6.5) EOF } diff --git a/tests/sandbox/test_sandbox_api.c b/tests/sandbox/test_sandbox_api.c index 90a2582da..e1a23b90d 100644 --- a/tests/sandbox/test_sandbox_api.c +++ b/tests/sandbox/test_sandbox_api.c @@ -12,7 +12,7 @@ * * Expected: all tests pass (exit 0). * - * See: PLAN.md (Tasks 2.2, 2.3) + * See: docs/changelog.md (Tasks 2.2, 2.3) */ #include diff --git a/tests/sandbox/test_sandbox_exec.sh b/tests/sandbox/test_sandbox_exec.sh index c1288520c..30b80cec0 100755 --- a/tests/sandbox/test_sandbox_exec.sh +++ b/tests/sandbox/test_sandbox_exec.sh @@ -6,7 +6,7 @@ # # Expected: all tests pass (exit 0). # -# See: PLAN.md (Task 2.1) +# See: docs/changelog.md (Task 2.1) set -u diff --git a/tests/syscall/test_utimensat.c b/tests/syscall/test_utimensat.c index 7b4482a8f..5cf57d071 100644 --- a/tests/syscall/test_utimensat.c +++ b/tests/syscall/test_utimensat.c @@ -20,8 +20,8 @@ * * Exit code 0 = all tests passed, nonzero = failure. * - * See: PLAN.md (Task 1.4) - * PLAN.md (Blocker B4) + * See: docs/changelog.md (Task 1.4) + * docs/changelog.md (Blocker B4) */ #include