Bläddra i källkod

Review pass: correct feasibility report against USM master and LFS 13.1

Verified every load-bearing citation against master b046afe and the 13.1
book, and fixed what did not hold:

- Line citations: Resolver.resolve is at Resolver.vala:233 (not 232); the
  ResourceFinder locate_lib block is 75-104 (not 88-126) and the
  locate_resource switch spans 13-64.
- Book counts: remapped userinput blocks are 563 (make 120, not 118; plus
  3 one-offs clean/locale-test/locale-full) and 83 un-remapped, 646 total.
  Package pages ch5/6/7/8 = 5/17/8/85 (115 pages, 91 distinct packages);
  ch3 lists 93 downloads, ~85 of them source tarballs; '~94 packages'
  replaced with ~90 package-pass scaffolds throughout.
- SBU section: stated the counting conventions; flagged that the gcc
  entity is '53 SBU (with tests)' so the ~100 SBU fin sum double-counts
  gcc test time and the 3-5 h no-tests estimate keeps that headroom; the
  kernel entity is '0.4-32 SBU, typically about 2.5'.
- Stage 5 re-derived from USM's own MANIFEST.usm: the beyond-LFS set is
  ~10 packages (adds libselinux, gobject-introspection, invercargill and
  invercargill-json, libsodium), not ~6; build order updated; stage-5
  wall clock 45-75 min and engineering time 4-6 days.
- Host audit corrected: makeinfo/texi2any and /usr/bin/yacc are ALSO
  absent (the old text claimed makeinfo exists); stage-0 recipe now
  installs gcc-c++ patch texinfo and creates the yacc symlink; the dnf
  closure lists libgee/vala/gobject-introspection.
- Removed hallucinated specifics (gcc 'limitl', glibc 'libc.picture',
  perl 'groff-era') in favour of real ones (gcc's ulimit test preamble,
  perl/python non-configure builds).
- Effort arithmetic fixed: the line items sum to 36-50 working days,
  i.e. ~7-10 engineer-weeks (was 6-9); exec summary, section 7.2 and the
  task breakdown now agree; install-script staging wording matches
  Transaction.vala/create_install_directory reality.
saddle-Clanker-Saddle 1 månad sedan
förälder
incheckning
7a1aea4245
1 ändrade filer med 149 tillägg och 80 borttagningar
  1. 149 80
      LFS_BOOTSTRAP_FEASIBILITY.md

+ 149 - 80
LFS_BOOTSTRAP_FEASIBILITY.md

@@ -16,7 +16,8 @@ be one USM resolution. It must be staged: a handful of driver invocations,
 each a valid single-pass resolution, chained by an orchestrator. The dominant
 risks are environmental, not architectural: device nodes and `/proc` inside a
 capability-restricted chroot, systemd's build requirements, and self-hosting
-USM itself (valac + glib are not in the LFS book).
+USM itself (valac + glib and several friends are not in the LFS
+book — see Stage 5).
 
 Everything below was verified against the USM checkout at commit `b046afe`
 ("Merge branch 'goal/dcba' of Tilo15/usm into master", branch `master`) and
@@ -34,7 +35,7 @@ checkouts.
   USM scaffolds, stage drivers, and OCI assembly scripts) plus a small
   beyond-LFS package set so USM can build itself.
 - **The architecture must be staged.** `Resolver.resolve`
-  (src/lib/Resolver.vala:232) processes `manage`+`build`+`runtime` phases in
+  (src/lib/Resolver.vala:233) processes `manage`+`build`+`runtime` phases in
   one pass and produces one topological order
   (src/lib/Resolver.vala:422-428) — a single resolution cannot express LFS's
   ch5/6 temporary tools → ch7 chroot tools → ch8 final system sequence, where
@@ -52,17 +53,18 @@ checkouts.
   the pipeline itself.
 - **Disk layout matters**: `/` (and `/tmp`) is a 17 GB overlay while
   `/workspace` has ~11 TB free — all LFS scratch trees must live under
-  `/workspace`. (The host also lacks `g++`/`patch` — two chapter-2
-  requirements — but the sandbox has root and dnf5, so stage 0's image
+  `/workspace`. (The host also lacks `g++`, `patch` and `makeinfo` —
+  three chapter-2 requirements — but the sandbox has root and dnf5, so
+  stage 0's image
   recipe closes that with one `dnf install` line; see §2.3.)
-- **Effort.** Roughly **6–9 engineer-weeks** for the pipeline (generator +
+- **Effort.** Roughly **7–10 engineer-weeks** for the pipeline (generator +
   stage drivers + OCI scripts + key management + CI), of which the single
-  largest item is the book-to-scaffold generator (94 packages, ~640 command
+  largest item is the book-to-scaffold generator (~90 packages, ~640 command
   blocks, needing curated per-package handling). Wall-clock for one full
   bootstrap run on this 28-core sandbox: **~3–5 hours without test suites,
   ~8–14 hours with** (SBU-derived; see §7). Artifacts published per run:
-  ~94 `.usmc` source packages, ~94 `.dcba` build artifacts, two signed
-  listings.
+  ~90 `.usmc` source packages (one per package-pass) and as many `.dcba`
+  build artifacts, plus the signed listings.
 
 ## 2. Verified platform facts
 
@@ -116,14 +118,22 @@ one-liner.
     (`chapter02/hostreqs.xml`) that *newer* gcc/binutils than the book's are
     untested and likely to break the build; equal is the intended case;
   - kernel 7.0 > minimum → glibc headers configure cleanly.
-- **Host tool gaps (new this session):** `/usr/bin/g++` and `cc1plus` are
-  **absent** (`gcc -x c++` fails: "cannot execute 'cc1plus'"), and `patch`
-  is absent. `find`/`makeinfo` exist where needed. The book's ch2 software
-  list requires both a C++ compiler and patch. `dnf5 5.4.3.0` is present and
-  both `gcc-c++.x86_64 16.2.1-2.fc44` (updates) and `patch.x86_64
-  2.8-4.fc44` (fedora) are available → the Stage-0 image build must
-  `dnf install gcc-c++ patch` (plus any other missing ch2 items re-checked
-  at image-build time).
+- **Host tool gaps (audited this session against the book's ch2 list):**
+  `g++`/`cc1plus` **absent** (`gcc -x c++` fails: "cannot execute
+  'cc1plus'"), `patch` **absent**, and `makeinfo`/`texi2any` (Texinfo)
+  **absent** — all three are ch2 requirements (Texinfo-5.0+ is on the
+  list; the glibc/gcc/perl builds invoke `makeinfo` if present and skip
+  documentation otherwise, but the book expects a compliant host).
+  `/usr/bin/yacc` is also absent (the book wants yacc as a bison link —
+  bison 3.8.2 itself is present, and the ch5 bison rebuild provides
+  `/usr/bin/yacc` inside the toolchain, so this only matters for the
+  stage-0 host surface). Everything else on the ch2 list is present:
+  bash 5.3.9, as/ld (binutils 2.46.1), bison, coreutils (incl. `nproc`),
+  diffutils (`diff`/`cmp`), findutils, gawk, gcc, grep, gzip, m4, make,
+  perl, python3, sed, tar, xz. `dnf5 5.4.3.0` is present and the missing
+  items are plain dnf installs (`gcc-c++`, `patch`, `texinfo`) → the
+  Stage-0 image build must `dnf install gcc-c++ patch texinfo` (plus a
+  ch2 re-check script at image-build time).
 
 ### 2.4 Container runtimes
 
@@ -152,7 +162,7 @@ in this session.
    pre-created, `usm.config` with `is_managed=true` +
    `managed.state_path`).
 2. **ResourceFinder local presence.** `ResourceFinder.locate_resource`
-   (src/lib/ResourceFinder.vala:13-58) resolves refs mostly **relative to
+   (src/lib/ResourceFinder.vala:13-64) resolves refs mostly **relative to
    `paths.destination`** (`locate_path`, `locate_res`, `locate_cfg`,
    `locate_bin` walks `$PATH` under destination, `locate_libres`,
    `locate_pc` including `PKG_CONFIG_PATH` entries, `locate_vapi`,
@@ -160,14 +170,14 @@ in this session.
    sees only the staged root. **Caveat (a real constraint, not a blocker):
    several locators are host-absolute** — `locate_lib` consults
    `ldconfig -p` and falls back to absolute `/lib /usr/lib /lib64
-   /usr/lib64` (src/lib/ResourceFinder.vala:88-126), and `locate_inc`,
+   /usr/lib64` (src/lib/ResourceFinder.vala:75-104), and `locate_inc`,
    `locate_libexec`, `locate_gir`, `locate_info/man/locale`,
    `locate_app` use absolute `/usr/...` paths. Inside a chroot that is
    correct (destination is `/`); *outside* a chroot with USM_DESTDIR set,
    `lib:`/`include:` refs can be satisfied by the host — the stage drivers
    must run usm inside the target chroot, or audited for it.
 3. **Resolver is single-pass.** `Resolver.resolve`
-   (src/lib/Resolver.vala:232-287) picks roots, does ONE batched SPM query,
+   (src/lib/Resolver.vala:233-287) picks roots, does ONE batched SPM query,
    processes all packages, and `resolution_phases` folds
    `manage`+`build`+`runtime` (src/lib/Resolver.vala:422-428) into one
    dependency graph with one topological install order. Missing resources
@@ -230,20 +240,28 @@ XML; the machine-readable part is excellent (`packages.ent`: 718 entities —
 names, versions, URLs, md5s, SBU/disk estimates as text), but the ~640
 build steps are prose. Counts from this checkout:
 
-- `userinput remap="…"` command blocks across ch5–8: **558** — 105
-  `configure`, 118 `make`, 180 `install`, 76 `pre`, 76 `test`, 3 `adjust`;
-  plus **85** un-remapped `<userinput>` (chapter setup commands).
-- Package-ish pages: ch5 5, ch6 17, ch7 8, ch8 91 files (85 with build
-  commands) → **~94 source packages** distinct (some pages are
-  multi-pass: gcc appears in ch5 pass1/pass2 and ch8; binutils pass1/ch8).
+- `userinput remap="…"` command blocks across ch5–8: **563** — 105
+  `configure`, 120 `make`, 180 `install`, 76 `pre`, 76 `test`, 3 `adjust`,
+  plus 3 one-offs (`clean`, `locale-test`, `locale-full`); plus **83**
+  un-remapped `<userinput>` (chapter setup commands) → 646 `<userinput>`
+  elements in total across ch5–8.
+- Package pages (procedural pages like `kernfs/chroot/createfiles`
+  excluded): ch5 5, ch6 17, ch7 8, ch8 85 → 115 pages, 91 distinct
+  packages (some are multi-pass: gcc appears as ch5 pass1/pass2 plus
+  `libstdc++` and ch8; binutils as ch5/ch6 pass1/pass2 and ch8).
+  Chapter 3 lists **93 downloadable URLs** (~85 of them source tarballs
+  with md5s, the rest docs/bootscripts/udev-lfs/kernel); the ~90
+  package-pass scaffolds in the executive summary exclude docs/extras
+  and count multi-pass packages once.
 
 A naive `<userinput>`-to-shell-literal extraction produces *almost* right
 scripts — but not right: entities need expansion, `make -j1` /
 `make -k check` conventions need policy, ch5/6 scripts must hard-code
 `/tools` prefixes and env (`LFS`, `LFS_TGT`, `PATH=/tools/bin:...`), ch8
 scripts assume the chroot, and a few packages need hand-massaging (gcc's
-three passes and its `limitl`, glibc's `libc.picture`-era configure
-options, systemd's `meson -D*` policy, perl's `groff`-era hints). So: a
+three passes plus its `ulimit -s -H unlimited` test preamble, glibc's
+long multi-line configure block, systemd's `meson -D*` policy, perl's
+and python's non-`./configure` build commands). So: a
 generator (Python, ~1.5-2k lines) with a **curated overrides table** for
 the ~15 hairy packages, emitting both the scaffolds and a
 `stage-manifest.json` the orchestrator consumes. Generated scaffolds are
@@ -256,14 +274,16 @@ the ch5/6/ch7/ch8 script envs), scaffold review pass 2-4 days.
 
 **Mapping.** The bootstrap container is the *build host* for stages 1-4: a
 root filesystem with a chapter-2-compliant toolchain, the `dnf`-installed
-gaps closed, `usm` + its runtime deps (curl, git, libsodium, glib, json-glib
-— via dnf on the Fedora base) preinstalled, and the LFS sources either
-pre-fetched or fetchable. Two ways to produce it:
+gaps closed, `usm` + its runtime/build closure (curl, git, libsodium, glib,
+libgee, json-glib, vala, gobject-introspection, and the two invercargill
+libraries — all via dnf on the Fedora base; see §5 Stage 5) preinstalled,
+and the LFS sources either pre-fetched or fetchable. Two ways to produce it:
 
 - **(a) dnf-based (recommended for stage 0).** Start from a Fedora
   container base (or `dnf --installroot` into a directory on this host —
-  no runtime needed), `dnf install` the ch2 checklist + `gcc-c++ patch` +
-  usm's runtime closure, then tar it up. Fully reproducible-ish, fast.
+  no runtime needed), `dnf install` the ch2 checklist +
+  `gcc-c++ patch texinfo` + usm's runtime closure, then tar it up.
+  Fully reproducible-ish, fast.
 - **(b) host-clone (proven here).** Clone the sandbox host root, add the
   missing pieces via dnf, prune to the ch2 list. Faster to stand up (the
   bison proof used exactly this), less hermetic; fine for development,
@@ -396,13 +416,16 @@ a full machine); every stage's output feeds the next.
 Purpose: a hermetic chapter-2-compliant build host. Recipe (dnf-based):
 
 1. `dnf --installroot=$ROOT --releasever=44 install @build-tools` or an
-   explicit list from the book's ch2 table, **plus `gcc-c++` and `patch`**
-   (the two gaps found on this sandbox — see §2.3), plus usm's runtime
-   closure: `curl git libsodium glib2 json-glib` (Fedora names).
+   explicit list from the book's ch2 table, **plus `gcc-c++`, `patch` and
+   `texinfo`** (the ch2 gaps found on this sandbox — see §2.3; `bison`
+   provides `yacc` only via a separate symlink the image recipe must
+   create), plus usm's runtime closure: `curl git libsodium glib2
+   json-glib libgee vala gobject-introspection` (Fedora names).
 2. Install `usm` itself (any build that runs on the host glibc — the
    Fedora dnf set is fine; this stage-0 copy never needs to be the
    self-hosted one).
-3. Fetch/verify the ~94 LFS sources (URLs+md5 from `packages.ent`), or
+3. Fetch/verify the ~93 LFS downloads listed in ch3 (URLs+md5 from
+   `packages.ent`; ~85 are source tarballs, the rest docs/extras), or
    vendor them into the image; pre-seed the file:// repo layout.
 4. Assemble the OCI image (script) — or just keep it as a directory tree
    for chroot use on this sandbox.
@@ -424,10 +447,13 @@ checkout and emits one scaffold dir per package-pass:
   `PATH`/`LFS_TGT` env set by the stage driver, `make -j$(nproc)`.
 - `usm-scripts/install.sh` — the book's install commands; DESTDIR-style
   staging is NOT what LFS does (it installs into `/`/`/tools` inside the
-  chroot), so install scripts write directly and USM's per-package
-  install staging is bypassed via `install_path == destination` (or the
-  scripts `cp` from a destdir; per-scaffold choice, recorded in the
-  generator's overrides).
+  chroot). USM runs install scripts with a per-package install dir as
+  `DESTDIR` (Transaction.vala passes `create_install_directory()` into
+  `run_install`, which overrides `destination` — Manifest.vala:441-455);
+  LFS scaffolds must therefore either install into that DESTDIR and let
+  the stage driver move its contents into place, or use the book's direct
+  paths (`/`/`/tools`) from inside the chroot. Per-scaffold choice,
+  recorded in the generator's overrides.
 - vendored patches from the book.
 
 Output: the scaffold git repo (reviewed, committed) → `*.usmc` via
@@ -462,12 +488,40 @@ system whose every file was placed by a USM transaction.
 
 ### Stage 5 — beyond-LFS self-hosting set + USM itself
 
-The book does not contain USM's build deps. The gap set (from the plan's
-evidence, re-confirmed against the book's package list): the book HAS
-pcre2 (chapter08/pcre.xml), libffi, python, meson, ninja, pkgconf,
-elfutils/libelf — so the beyond-LFS set is small: **glib, libgee, valac,
-json-glib, curl** (+ optional git). Order matters: glib → libgee →
-json-glib → valac → USM. Then `usm install usm` (from a `.usmc` of USM's
+The book does not contain USM's build deps. The gap set, re-derived this
+session from USM's own `MANIFEST.usm` (which is the authoritative
+dependency list) plus its direct library deps: the book HAS pcre2
+(chapter08/pcre.xml), libffi, python, meson, ninja, pkgconf, zlib, xz,
+util-linux (libmount/libblkid), elfutils/libelf. **libselinux is NOT
+in the book**, yet USM's manifest names `lib:libselinux.so.1` as a
+runtime ref (it arrives via glib's linkage on a typical distro).
+Beyond-LFS set, from the manifests:
+
+- **glib** (glib/gobject/gio/gmodule; pulls libffi+pcre2+util-linux from
+  the book, plus **libselinux** which must be added beyond-LFS — or glib
+  rebuilt `-Dselinux=disabled`, in which case USM's `lib:libselinux.so.1`
+  runtime ref needs relaxing),
+- **libgee**, **json-glib**, **valac**,
+- **invercargill** and **invercargill-json** (USM's own base libraries —
+  `lib:libinvercargill-1.so`, `pc:invercargill-1.pc`,
+  `pc:invercargill-json.pc` are hard runtime/build refs of USM; both are
+  vala/meson packages needing only glib+gobject, so they slot after
+  json-glib),
+- **curl** (http(s) repositories at runtime; `usm dcba add-repo` for
+  remote repos too) (+ optional git),
+- **libsodium** (USM's ed25519 signing links `libsodium`; build dep via
+  vapi, runtime `lib:libsodium.so.26`),
+- **gobject-introspection** — USM's build deps include
+  `pc:gobject-introspection-1.0.pc` (optional in principle but currently
+  required by the manifest); it needs glib and Python, both present by
+  this point in stage 5.
+
+So the honest count is **~10 beyond-LFS packages** (glib, libselinux or a
+glib flag change, libgee, json-glib, gobject-introspection, valac,
+invercargill, invercargill-json, libsodium, curl), not the ~6 the plan's
+evidence suggested. Order: glib (+libselinux) → libgee → json-glib →
+gobject-introspection → valac → invercargill → invercargill-json →
+libsodium → curl → USM. Then `usm install usm` (from a `.usmc` of USM's
 own repo — USM's MANIFEST.usm exists at the repo root) is the
 self-hosting milestone: the distro's package manager installs itself.
 
@@ -482,7 +536,7 @@ verification, or skip assembly entirely and use `usm manifest deploy`.
 
 ## 6. Why staged (the resolver argument, precisely)
 
-`Resolver.resolve` (src/lib/Resolver.vala:232) computes ONE resolved set
+`Resolver.resolve` (src/lib/Resolver.vala:233) computes ONE resolved set
 with ONE topological order over `manage`+`build`+`runtime` refs
 (src/lib/Resolver.vala:422-428). LFS needs three orders interleaved:
 binutils-p1 → gcc-p1 → (ch5 libs) → gcc-p2 must complete *before* ch7's
@@ -502,17 +556,23 @@ commands and stock resolver error reports.
 
 ### 7.1 Wall-clock (build time)
 
-SBU data is machine-readable in `packages.ent` (entities
-`<pkg>-<tmp|fin>-sbu`). Summed this session (test-suite entities excluded,
-"less than X" counted as X/2):
-
-- ch5/6 temporary tools (`-tmp-sbu`, 33 entries): **~21 SBU**
-  (gcc-p2 5.2, gcc-p1 4.3, tcl 2.9, gettext 1.5, glibc-tmp 1.3, …).
-- ch8 final system (`-fin-sbu`, 82 entries): **~100 SBU**
-  (gcc 53, glibc 11, vim 3.3, python 2.7, e2fsprogs 2.4, …).
-- Linux kernel (~32 SBU) is *not* part of the bootstrap (no boot
-  requirement for a container/chroot distro); building it doubles the
-  gcc-dominated budget if wanted.
+SBU data is machine-readable in `packages.ent` (entities named
+`<pkg>-<tmp|tmpp1|tmpp2|fin>-sbu`). Summed this session (separate
+test-suite entities excluded, "less than X" counted as X/2, e2fsprogs'
+spinning-disk figure taken):
+
+- ch5/6 temporary tools (`-tmp*`-sbu entities: 33 of them, counting
+  binutils/gcc pass1+pass2): **~21 SBU** (gcc-p2 5.2, gcc-p1 4.3, tcl 2.9,
+  gettext 1.5, glibc-tmp 1.3, …).
+- ch8 final system (`-fin-sbu`, 82 entries): **~100 SBU** (gcc 53,
+  glibc 11, vim 3.3, python 2.7, e2fsprogs 2.4 on spinning disk / 0.4 on
+  SSD, …). **The gcc entity is explicitly "53 SBU (with tests)"** — the
+  book publishes no tests-free gcc figure, so the sum double-counts gcc's
+  tests as if they were build time; treat ~100 as an upper bound for
+  build-only; the no-tests estimate below keeps that headroom.
+- Linux kernel ("0.4 - 32 SBU, typically about 2.5 SBU") is *not* part of
+  the bootstrap (no boot requirement for a container/chroot distro);
+  adding it costs ~2.5 SBU typical / up to ~32 SBU worst-case.
 
 One SBU = one core's binutils-p1 time, typically **~1-2 min** on modern
 hardware (the book: gcc ~5 min "on the fastest systems"). With ~120 SBU
@@ -521,12 +581,14 @@ total and `make -j28`, the parallelisable `make` phases compress roughly
 (glibc's `make install`, gcc's early stages) do not. Honest estimate for
 one full stage-0→4 pass **on this 28-core sandbox**:
 
-- without test suites: **~3-5 hours** (gcc dominates: its 53 SBU ≈
-  1.5-2 h alone at j28 with serial configure/install).
+- without test suites: **~3-5 hours** (gcc dominates; discounting a
+  third to a half of its 53 SBU as test time still leaves ≈ 1-2 h alone
+  at j28 with serial configure/install — inside this range).
 - with the book's recommended test suites: **~8-14 hours** (tests are
   mostly serial and several are multi-SBU).
-- Stage 5 (beyond-LFS + USM): ~30-45 min (glib ~0.5 SBU, valac ~2 SBU,
-  USM itself < 1 SBU at j28).
+- Stage 5 (beyond-LFS, ~10 packages + USM): ~45-75 min (glib ~0.5 SBU,
+  valac ~2 SBU, gobject-introspection ~1-2 SBU, curl ~1 SBU, the two
+  invercargills and USM itself well under 1 SBU each at j28).
 - A DCBA-cached re-run of stage 4 (iteration loop): **~20-40 min** — this
   is the number that matters for daily development.
 
@@ -546,13 +608,15 @@ USM and LFS):
 - DCBA publish driver + per-stage repos: **2-4 days**
 - Stage orchestrator (chroot mgmt, /dev workarounds, checkpoints):
   **1-1.5 weeks**
-- Beyond-LFS set (6 packages, valac ordering) + USM self-host: **3-5 days**
+- Beyond-LFS set (~10 packages, valac + gobject-introspection
+  ordering) + USM self-host: **4-6 days**
 - OCI final image + smoke tests + documentation: **2-3 days**
 - Integration/debug of the first full run + book-drift tooling
   (regeneration diff): **1 week**
 
-**Total: ~6-9 engineer-weeks** (the spread is almost entirely the
-generator: 94 packages × curated overrides vs mostly-mechanical).
+**Total: ~7-10 engineer-weeks** (summing the lines above at 5-day weeks:
+36-50 working days; the spread is almost entirely the generator: ~90
+packages × curated overrides vs mostly-mechanical).
 Uncertainty: a first full run will surface 5-15 per-package script bugs
 (extraction artefacts, env differences) — that's inside the integration
 line above; a systemd-revision build adds ~3-5 days if pursued (and needs
@@ -588,12 +652,16 @@ a full host for /proc reasons, §8).
 - **Book drift.** LFS 13.1 → 13.2+ bumps package versions and commands.
   Mitigation: the generator is the only thing that reads the book;
   regeneration diff (git diff of scaffolds) makes drift reviewable.
-- **valac bootstrap ordering.** valac needs glib+libgee; USM needs valac
-  + glib + json-glib. Fixed linear order in stage 5 (glib → libgee →
-  json-glib → valac → USM); no cycle exists because valac's own
-  compilation can use a stage-0-hosted valac (dnf-installed) — the dnf
-  set already includes one, so USM's first self-host can even avoid
-  valac-from-source if desired.
+- **valac bootstrap ordering.** USM needs valac + glib + libgee +
+  json-glib + invercargill ×2 (+ libsodium, gobject-introspection at
+  build time). valac's own build (per its LFS-blfs recipe and the dnf
+  package) needs glib (libgee is a common *consumer*, not a valac
+  prerequisite). Fixed linear order in stage 5 (glib → libgee →
+  json-glib → gobject-introspection → valac → invercargill →
+  invercargill-json → libsodium → curl → USM); no cycle exists because
+  valac's compilation can use a stage-0-hosted valac (dnf-installed) —
+  the dnf set already includes one, so USM's first self-host can even
+  avoid valac-from-source if desired.
 - **ed25519 key management.** `keys/` 0600, private keys never leave the
   producer; `.dcbar`/`.usmr` carry the public key. Risk: losing the key
   orphanes a repo — keep one offline master per distro release line, and
@@ -602,11 +670,12 @@ a full host for /proc reasons, §8).
   scratch under /workspace (11 TB). Checkpoint tars double usage
   transiently; clean between stages.
 - **gcc/binutils newer than the book** are explicitly unsupported by LFS
-  (ch2 warning). This host's gcc 16.2.1 == book's 16.2.0 line — fine;
-  any host bump past the book needs a book bump first. Relatedly, the
-  sandbox host itself lacks `g++`/`patch` (ch2 requirements) — with root
-  and dnf5 this is a non-event: stage 0 installs them, and a ch2-check
-  script fails fast on any host still missing items.
+  (ch2 warning). This host's gcc 16.2.1 sits on the book's 16.2.0 line
+  and its binutils 2.46.1 is *older* than the book's 2.47 — fine; any
+  host bump past the book needs a book bump first. Relatedly, the
+  sandbox host lacks `g++`/`patch`/`makeinfo` (ch2 requirements) — with
+  root and dnf5 this is a non-event: stage 0 installs them, and a
+  ch2-check script fails fast on any host still missing items.
 
 ## 9. Recommended task breakdown (for the implementation goal)
 
@@ -616,7 +685,7 @@ Ordered; each is independently verifiable and lands value on its own:
    bootstrap image from dnf; verify by chroot + book ch2 script.
 2. **Generator v1: packages.ent → scaffold skeletons** (3-5 d) — manifest
    fields, URLs/md5s, acquire scripts; build.sh placeholders; no commands
-   yet. Deliverable: 94 scaffolds packaging cleanly (`usm manifest
+   yet. Deliverable: ~90 scaffolds packaging cleanly (`usm manifest
    package` all green).
 3. **Generator v2: chapter XML → build/install scripts + overrides
    table** (1.5-2 wk) — ch5/6 (tmp passes, /tools env), ch7, ch8;
@@ -628,8 +697,8 @@ Ordered; each is independently verifiable and lands value on its own:
    repos.
 6. **Stage 4: final-system build via USM** (3-5 d) — full ch8 through usm
    transactions with DCBA caches.
-7. **Stage 5: beyond-LFS set + USM self-host** (3-5 d) — the milestone:
-   `usm install usm` inside the final root.
+7. **Stage 5: beyond-LFS set (~10 packages) + USM self-host** (4-6 d) —
+   the milestone: `usm install usm` inside the final root.
 8. **Stage 6: final OCI image + smoke tests** (2-3 d) — assembled image,
    chroot/podman verification matrix, docs.
 9. **(Follow-up, separate goal) systemd revision, kernel build, https
@@ -644,5 +713,5 @@ enough for a generator with a modest overrides table, and the two hard
 environmental constraints of this sandbox (no device-node creation, no
 mounts) are worked around for every stage except a systemd build. The
 recommended first implementation milestone is tasks 1+2: a verified
-bootstrap image and 94 packaging-clean scaffolds — roughly a week of
+bootstrap image and ~90 packaging-clean scaffolds — roughly a week of
 work that de-risks everything downstream.