- **2026-06-05 — P6p (`res_init.c` resolver-state bootstrap) merged.** Ported `ircd/res_init.c` outright to `ircd-common/res_init.rs` → `res_init.o` dropped; only `res.c` (the resolver proper) is still C in P6.
  - **Cluster choice.** The smaller, self-contained of the two remaining DNS files. `nm cbuild/res_init.o` defines `ircd_res` (B), `ircd_res_init`/`ircd_res_randomid` (T), and the file-static `res_setoptions` (`.isra.0`); `nm --undefined-only` shows zero ircd-internal deps — only libc + `inetpton` (the P1 `support.rs` symbol). Same self-contained-leaf shape as P6n/P6o → drop the `.o` (add to `PORTED`), no `_link.o`.
  - **Config-resolved body.** Verified `DEBUG`/`NEXT`/`RESOLVSORT`/`RFC1535`/`HAVE_GETIPNODEBYNAME`/`USELOOPBACK`/`__BIND_RES_TEXT` all OFF in `cbuild/{setup,config}.h` + `build.rs` defines. So: `ircd_res` is a plain zero-init BSS global (no `= {RES_TIMEOUT,}`); `ircd_res_init` is the simple path only (defaults → `LOCALDOMAIN` block → `/etc/resolv.conf` parse loop with the `MATCH` macro + `inetpton(AF_INET6,…)` nameservers → `gethostname` fallback → the `#ifndef RFC1535` default-`dnsrch` derivation `LOCALDOMAINPARTS=2`/`MAXDFLSRCH=3` → `RES_OPTIONS` env → `options |= RES_INIT`), no NetInfo/`ircd_netinfo_res_init`, no sortlist; `res_setoptions` handles `ndots:` (clamp to `RES_MAXNDOTS=15`), `inet6` (→`RES_USE_INET6=0x2000`), `debug` (no-op, DEBUG off); `res_randomid` = `0xffff & (tv_sec ^ tv_usec ^ getpid())`.
  - **Faithfulness.** Raw-pointer port mirroring the C: `strncpyzt`→`strncpy`+NUL; the two near-identical search-list tokenizers inlined faithfully (the `LOCALDOMAIN` one breaks on `\n`, the file `search` one doesn't); `dnsrch[]` pointers walk into `defdname`; nsaddr `sin6_addr`=in6addr_any (16 zero bytes) / `sin6_family`=AF_INET6 / `sin6_port`=htons(53); `ndots` written via the bindgen `set_ndots` bitfield accessor. `ircd_res` defined as `#[no_mangle] static mut` via `MaybeUninit::zeroed().assume_init()` (the `s_misc.rs::ircst` idiom); `res_mkquery.rs`/`res.c`/`s_bsd.c` keep importing `ircd_res`/`ircd_res_init` from bindgen and resolve to the Rust defs at link (the conf/kconf-globals mechanism).
  - **Classification.** Utility/leaf (none in `msgtab`); no `IsServer`/remote-field path.
  - **L1.** `ircd-testkit/tests/res_init_diff.rs`: zero diff vs `cref_ircd_res_init`. Both `ircd_res`/`cref_ircd_res` zeroed + `.id` pinned `0x4321` per test (skip the nondeterministic `res_randomid`); env-set + both inits + env-restore under a process-global `RES_LOCK` (parallel `#[test]` threads share the env + globals). The result structs diffed field-by-field (scalars/bitfields, 3× `nsaddr_list` `sockaddr_in6` bytes, `defdname` bytes, `dnsrch[]` as offsets into `defdname`). The differential makes the `/etc/resolv.conf`+`inetpton` path host-robust (both read the same file → identical, or both skip it); the host-independent branches run on every CI host via env — `plain` (default-`dnsrch`), `LOCALDOMAIN` (search tokenizer), `RES_OPTIONS` `ndots:3 inet6` and `ndots:99` (the `RES_MAXNDOTS` clamp). `res_randomid` nondeterministic → only `& 0xffff` smoke-tested (the one symbol with no L1 diff).
  - **L2/S2S.** None — pure leaf, no client/server command path under `NO_DNS_LOOKUP`. Boot+registration (`golden_registration`) and the resolver-adjacent `golden_s_dns` goldens stay byte-identical, confirming the Rust `ircd_res_init`/`ircd_res` (called from `s_bsd.c` at startup) boot the server identically to reference-C. End-to-end DNS coverage (L2/L4) arrives when the resolver proper `res.c` is ported under a fixture nameserver.

- **2026-06-05 — P6q (`res.c` cache-hash leaves) merged.** First bite of the `res.c` port — the last C file. `res.c` is one large connected cluster (resolver socket + request queue + hostent cache) over file-statics with only 11 `_ext.h`-exported entry points; the dependency floor is the **hostent cache**, whose foundation is the two pure hash leaves `hash_number`/`hash_name`. Ported bottom-up so the cache core (`make_cache`/`update_list`/`find_cache_*`/`rem_cache`, P6r+) can build on Rust hashes.
  - **Cluster choice** — only `hash_number`/`hash_name` (pure: read solely their argument, touch no module state → zero data-global exposure), keeping this bite about establishing the new `res.c` static seam on a trivially verifiable pair before the gnarly cache core.
  - **Guard / seam** — new three-way `RES_CACHE_LINKAGE` macro in `res.c`: `extern` under `-DPORT_RES_CACHE_P6q` (res_link.o LINK build: bodies `#ifndef`'d out → Rust defines them, still-C walkers resolve to Rust at link), empty under `-DRES_CACHE_EXPOSE` (cref oracle res.o: GLOBAL → `cref_hash_*` for L1), else `static` (plain). `build.rs`: res_link.o gains `-DPORT_RES_CACHE_P6q`; a new step recompiles the cref `res.o` with `-DRES_CACHE_EXPOSE` (overwrites the plain one — res.o is consumed only by `libcref.a`, the link set uses res_link.o, so the real binary is untouched). RED confirmed: dropping the C bodies left 8 undefined `hash_name`/`hash_number` refs from the still-C walkers (res.c:1084/1088/1282/1316/1337/1382/1583/1600) — the exact port checklist.
  - **Config-resolved body** — neither function is config-gated; `ARES_CACSIZE`=1009.
  - **Faithfulness** — `hash_number`: `hashv = ip[0]; for i in 1..16 { hashv = 2*hashv + ip[i] }` over a **wrapping `u_int`** (the doubling overflows 32 bits for large addresses — `% ARES_CACSIZE` applied only at the end), reads exactly 16 bytes (sizeof struct in6_addr; AFINET=AF_INET6) → `wrapping_add`/wrapping double. `hash_name`: sum bytes until the first `.`/NUL, `% 1009`; `char` is **signed** on x86-64, so a high-bit byte promotes to a negative `int` before the unsigned `+=` — replicated via `*p as i32 as u32` + `wrapping_add` for 8-bit-clean parity. Mutable `*mut` args (C `u_char*`/`char*`). Returns `c_int` in `[0,1009)`.
  - **Classification** — utility/leaf, **not in `msgtab`**, no client/server command path (the resolver is inert under `NO_DNS_LOOKUP`; the cache never populates at runtime).
  - **L1** — `ircd-testkit/tests/res_hash_diff.rs`: Rust `hash_number`/`hash_name` (`#[link_name]` → the only def once res_link.o drops the C bodies) vs `cref_` over crafted corpora — 16-byte v6 addrs (zero, all-0xff overflow, single-bit, ::ffff: v4-mapped, every-byte-high-bit, random), names (empty, dot-first, no-dot, embedded-dot, case-sensitive, UTF-8/0xff high-bit, long label); each result range-checked `[0,1009)`. Zero diff. Both pure → no global state / no `RES_LOCK`.
  - **L2 / S2S** — none. No client/server command reaches these under the locked config; `golden_s_dns` + the registration/boot goldens (which boot the full ircd + still-C resolver, exercising the C cache walkers that now call the Rust hashes) confirm no regression. End-to-end DNS coverage arrives when the resolver proper is ported under a fixture nameserver.

- **2026-06-05 — P6r (`res.c` cache removal/expiry core) merged.** Second bite of the `res.c` port (after P6q's `hash_number`/`hash_name`). Ported the **removal/expiry** connected component — the half of the hostent cache that *deletes* entries — to `ircd-common/res.rs`, built directly on the P6q Rust hashes. `res.c`'s only remaining strands are the resolver proper (socket + request queue + answer parser) and the cache **build/insert/lookup** half.
  - **Cluster choice.** Bottom-up from the cache floor: `rem_list` (static), `rem_cache` (static), `expire_cache` (global, res_ext.h), `flush_cache` (global, res_ext.h). They form a clean connected component — `expire_cache`/`flush_cache` both call `rem_cache`; `rem_cache`/`rem_list` call only the P6q hashes + touch the cache data structures — with no dependency on the still-C build/insert/lookup half (`add_to_cache`/`update_list`/`find_cache_*`/`make_cache`). The two public entry points give clean `cref_` L1 symbols; the two statics are exposed via the generalized `RES_CACHE_LINKAGE`.
  - **Seam.** `-DPORT_RES_CACHE_REM_P6r` added to the `res_link.o` recipe. `RES_CACHE_LINKAGE` now fires `extern` under either `PORT_RES_CACHE_P6q` **or** `PORT_RES_CACHE_REM_P6r` (res_link.o defines both), so the `rem_cache`/`rem_list` forward decls + definitions de-static; their bodies (+ `expire_cache`/`flush_cache`) are wrapped in `#ifndef PORT_RES_CACHE_REM_P6r`. RED (after fixing a static-vs-non-static decl mismatch on the definitions) was the four undefined refs: `flush_cache` (Rust `s_conf.rs` rehash), `expire_cache` (C `ircd.c:1195`), `rem_list` (C `res.c:1379` find_cache_number), `rem_cache` (C `res.c:1119` add_to_cache) — the exact port checklist. GREEN confirmed via `nm target/debug/ircd` (all four `T` from Rust).
  - **Data-global exposure.** The Rust removal core + the L1 live world need `cachetop`/`cainfo` (already `RES_DNS_STATIC`-exposed for m_dns) **plus** `hashtable[]`/`incache`. `RES_DNS_STATIC` now also blanks `static` under `RES_CACHE_EXPOSE` (so the cref oracle res.o exposes `cref_cachetop`/`cref_cainfo`/`cref_hashtable`/`cref_incache`/`cref_reinfo` for the L1 isolated worlds), and `hashtable`/`incache` moved onto it. In res_link.o (which has `PORT_DNS_M_DNS`) all four stay **defined-global** — C owns the storage, the Rust core + the L1 live world extern them, same as P6k's `conf`. No extern/definition split needed (so no `= NULL`/array-initializer churn). `res.rs` adds `static mut hashtable: [CacheTable; 1009]` + `static mut incache: c_int` to its extern block + `local_base()`/`hashtable_base()` helpers.
  - **Faithfulness.** Raw-pointer walks mirroring the C `aCache **` chains exactly. `rem_list` captures `cp->list_next` first, unlinks from `cachetop` only, `MyFree`s, returns the saved successor — and (the C quirk preserved) does **not** decrement `incache`, touch `cainfo`, or unlink the hash buckets (leaving the freed entry's bucket refs dangling). `rem_cache`'s hostp-clearing loop walks `local[]` from `highest_fd` down; the bucket-by-`hash_name`/`hash_number` unlinks use `hp->h_addr` = `h_addr_list[0]`; `MyFree(x)` → guarded `libc::free` (the macro's re-NULL is moot — the struct is freed; `*h_addr_list` is one contiguous block freed once, then the pointer array). `expire_cache` captures `list_next` before the possible free and returns `(next > now) ? next : now + AR_TTL`.
  - **Classification.** Utility/leaf — none in `msgtab`. **L1 only** — the resolver is inert under `NO_DNS_LOOKUP`, the cache never populates at runtime, so no client/server command path reaches these. No S2S (no `IsServer`/remote-field path).
  - **L1** (`ircd-testkit/tests/res_cache_rem_diff.rs`): the P6k two-isolated-worlds pattern. A `World` bundles fn-pointers + global base pointers (`cachetop`/`hashtable`/`incache`/`cainfo` **and** `local`/`highest_fd` — the hostp loop reads s_bsd globals, which are renamed to `cref_local`/`cref_highest_fd` in the archive, so each world must own its own). Entries are hand-built mimicking `add_to_cache` (one contiguous 16×n address block with `h_addr_list[i]` pointing into it + per-name strdup'd aliases, so `rem_cache`'s frees match `make_cache`'s layout), then the public fns are driven and `Vec<i64>` observations asserted equal. 4 tests, zero diff: **flush_cache** (3 mixed single/multi-alias/multi-addr entries → `cachetop`/buckets empty, `incache` delta 0, `ca_dels`==3; round-trip re-insert + re-flush clean), **expire_cache** (two past + two future at `now=5000` → past removed, survivors kept in order, `incache` -=2, `ca_expires` delta 2, return = min-future 7000; the all-future inverse returns min expireat not `now+AR_TTL`; empty-cache returns `now+AR_TTL`), **rem_cache hostp** (a per-world `local[0]->hostp` at the doomed entry is NULLed, an unrelated `local[1]->hostp` untouched), **rem_list** (returns the successor, unlinks the middle from `cachetop`, `incache` **unchanged** — the leak-by-design quirk — then head + last removal empties the list). A process-global `Mutex` (poison-tolerant) serializes the `#[test]`s (shared cache + s_bsd globals).
  - **No L2/S2S.** Resolver inert under `NO_DNS_LOOKUP`; `golden_s_dns` (oper DNS stats + empty `DNS l` cache dump) + `golden_registration` stay byte-identical, confirming the still-C `add_to_cache`/`find_cache_number` now call the Rust `rem_cache`/`rem_list` (+ the P6q hashes) without regression. End-to-end DNS coverage arrives when the resolver proper is ported under a fixture nameserver.
  - **Plan** — `docs/superpowers/plans/2026-06-05-p6r-res-cache-rem.md`.

- **2026-06-05 — P6s (`res.c` cache lookup/reorder half) merged.** Third bite of the `res.c` port (after P6q's hashes + P6r's removal core). Ported the **lookup/reorder** connected component — the half of the hostent cache that *finds* entries and *reorders/merges* them — to `ircd-common/res.rs`. `res.c`'s only remaining strands are the resolver proper (socket + request queue + answer parser) and the cache **build/insert** half (`make_cache`/`add_to_cache`, → P6t).
  - **Cluster choice.** Bottom-up from the cache floor: `update_list` (static) is the shared leaf both finders call; `find_cache_name`/`find_cache_number` (static) are the lookup entry points. A clean connected component — the finders call `update_list` (+ the already-Rust P6r `rem_list` in `find_cache_number`'s degenerate branch) and touch only `cachetop`/`hashtable`/`cainfo` + `mycmp`/`MyRealloc`/`mystrdup`; no dependency on the still-C build/insert half. Splitting the cache "build/insert/lookup" remainder into lookup (P6s) then build/insert (P6t) keeps the bite small, matching the P6q/P6r cadence.
  - **Seam.** `-DPORT_RES_CACHE_LOOKUP_P6s` added to the `res_link.o` recipe; the three-way `RES_CACHE_LINKAGE` now fires `extern` under `PORT_RES_CACHE_P6q` **or** `PORT_RES_CACHE_REM_P6r` **or** `PORT_RES_CACHE_LOOKUP_P6s` (res_link.o defines all three guards), GLOBAL under `RES_CACHE_EXPOSE` (cref oracle), else static. The three forward decls (res.c:84/85/91) + definitions flip `static` → `RES_CACHE_LINKAGE`; the bodies (update_list … find_cache_number, res.c:1140–1406) wrap in `#ifndef PORT_RES_CACHE_LOOKUP_P6s`. RED was the undefined-symbol link error for `find_cache_name`/`find_cache_number` (still-C callers `gethost_byname_type`:410, `proc_answer`:776, `gethost_byaddr`:440, `make_cache`:1440/1456 — the exact port checklist; `update_list` had no still-C reference once the finders dropped, but Rust defines it for the finders to call). GREEN via `nm target/debug/ircd | grep -E ' T (update_list|find_cache_name|find_cache_number)$'`. (A C-comment gotcha: `gethost_*/proc` and `find_cache_*/cref` each contain a literal `*/` that prematurely closed the seam comment → reworded.)
  - **Config-resolved bodies.** No gates touch these (`DEBUG` off → the `Debug(...)` blocks vanish). `IN_ADDR`=`in6_addr` (sizeof 16, AFINET=AF_INET6); `WHOSTENTP(x)`=OR of the 16 `s6_addr` bytes; `S_ADDR`=`s6_addr`. `ResRQ.he` is the inline `hent` (`h_addr_list:[in6_addr;35]`, `h_aliases:[*char;35]`); `aCache.he` is the libc `hostent` (`**char` lists). Constants T_A=1/T_PTR=12/T_AAAA=28, MAXALIASES/MAXADDRS=35, FLG_*.
  - **Faithfulness.** Raw-pointer translation of the C for-loops with post-increment index walks (`for (i=0,s=h_name; s; s=h_aliases[i++])` → `s = *aliases.offset(i); i+=1` at the loop tail), incl. the `find_cache_name` full-list fallback's **fixed-`s`=alias[0] quirk** (`s` set only in the init, never in the increment → it only ever compares alias[0]). `update_list`'s T_PTR alias-merge reallocs `h_aliases` by one + `mystrdup`s the new name; the T_A/T_AAAA addr-merge reproduces the double-`MyRealloc` exactly — realloc the contiguous IP block (`*h_addr_list[0]`) to `addrcount*16`, realloc the pointer array to `addrcount+1`, repoint each pointer into the (possibly moved) block, NULL-terminate, `bcopy` the new IP into the last slot (`*--ab`) — then the `addrcount>1` → clear `FLG_PTR_FWD|VALID` vs the single-addr `FLG_PTR_PEND_REV`/`FLG_PTR_PEND_FWD` logic. `find_cache_number`'s second loop preserves the C for-loop `continue`→increment semantics (every guard incl. the `cp = rem_list(cp)` degenerate-reap falls through to the trailing `cp = cp->list_next`). `bcmp`→16-byte slice compare; `MyRealloc`/`mystrdup`/`mycmp` are the Rust ports resolved at link.
  - **Classification.** Utility/leaf — none in `msgtab`. → **L1 only.** No `IsServer`/remote-field path; the resolver is inert under `NO_DNS_LOOKUP` so no client/server command populates the cache at runtime.
  - **L1** (`ircd-testkit/tests/res_cache_lookup_diff.rs`): the P6k/P6r two-isolated-`World`s pattern (fn-pointers + global base pointers: `cachetop`/`hashtable`/`cainfo` + `find_cache_*`/`update_list`/`hash_*`). `insert()` builds entries à la `add_to_cache` (contiguous addr block); `make_rptr()` builds a `ResRQ` per world (each owns its pointers → compare CONTENTS not pointers). 6 tests, zero diff: **update_list reorder-only** (rptr=NULL: head move keeping relative order, `ca_updates` bump, repeat on the head = no-op); **find_cache_name** hashed hit + reorder + `ca_na_hits` / name-miss (counter unchanged, order kept) / flags-gate miss / the alias full-list fallback (h_name & alias[0] in distinct hash buckets); **find_cache_number** hashed hit + `ca_nu_hits` / absent-addr miss / FLG_PTR_VALID gate miss / the multi-addr fallback (lookup the 2nd of two addresses, found only via the full-list scan); **update_list T_PTR alias merge** (append a new name) + the already-present (= cached h_name) no-growth inverse; **update_list T_A addr merge** (append a new IP, addrcount→2 flag clear) + the already-present no-growth inverse (single-addr `FLG_PTR_PEND_FWD` set). A process-global `Mutex` serializes the `#[test]`s; each scenario `reset()`s its world's cache + buckets and snapshots counter deltas.
  - **No L2/S2S.** Resolver inert under `NO_DNS_LOOKUP`; `golden_s_dns` (oper DNS stats + empty `DNS l`) + `golden_registration` stay byte-identical, confirming the still-C `proc_answer`/`gethost_*`/`make_cache` now call the Rust lookup/reorder core (+ the P6q hashes + P6r removal) without regression. End-to-end DNS coverage arrives when the resolver proper is ported under a fixture nameserver.
  - **Plan** — `docs/superpowers/plans/2026-06-05-p6s-res-cache-lookup.md`.

- **2026-06-05 — P6t (`res.c` cache build/insert half `add_to_cache`/`make_cache`) merged.** The fourth and final bite of the `res.c` hostent-cache cluster — the two functions that *create* and *link in* cache entries — closing the cache out: with this **`res.c`'s hostent cache is now FULLY Rust**, and only the resolver proper (request queue + answer parser + socket I/O) remains C.
  - **Cluster choice / why now.** The dependency floor was already in place: P6q (the `hash_number`/`hash_name` leaves), P6r (the `rem_list`/`rem_cache`/`expire_cache`/`flush_cache` removal core), P6s (the `update_list`/`find_cache_name`/`find_cache_number` lookup/reorder half). `add_to_cache`/`make_cache` are the *only* remaining cache functions and form a tight connected component (`make_cache`→`find_cache_*`/`MyMalloc`→`add_to_cache`→`rem_cache`), every callee already Rust → the natural last bite.
  - **Guard / partial port.** `#ifndef PORT_RES_CACHE_BUILD_P6t` wraps both C bodies (res.c:1086 `add_to_cache`, res.c:1416 `make_cache`); `-DPORT_RES_CACHE_BUILD_P6t` added to the `res_link.o` compile in `build.rs` alongside the P6q/r/s defines. Both functions' decl+def flipped from `static` to the existing three-way `RES_CACHE_LINKAGE` macro (→ `extern` under any `PORT_RES_CACHE_*` in res_link.o so the still-C `proc_answer` caller of `make_cache` resolves to the Rust def; → GLOBAL under `-DRES_CACHE_EXPOSE` in the cref oracle res.o so `cref_add_to_cache`/`cref_make_cache` exist for L1). `make_cache`'s forward decl at res.c:88 updated to match.
  - **Config-resolved body.** `DEBUG` off → all `Debug((…))` blocks drop out (no `inetntop`/`ipv6string` debug formatting in `add_to_cache`). `AFINET=AF_INET6` → `struct IN_ADDR` = `in6_addr` (16 bytes); `WHOSTENTP` tests all 16 `s6_addr` bytes; `make_cache`'s address-count loop + contiguous-block fill use `sizeof(struct IN_ADDR)` = 16. `MAXCACHED 512`, `MAXADDRS`/`MAXALIASES 35`.
  - **Faithfulness notes.** `make_cache` **steals** the ResRQ's alias pointers (`rptr->he.h_aliases[n]` → cache, source nulled) and `h_name` (nulled after copy) — preserved exactly, so the L1 asserts the source ResRQ fields go NULL. The `*t++ = s; s += sizeof(IN_ADDR)` two-array block-fill is reproduced with raw-pointer `t.add(1)`/`s.add(IN_ADDR_SIZE)` walks; `bzero` → `write_bytes(_,0,_)`. The `ttl < 600` clamp bumps `reinfo.re_shortttl` and forces 600; `expireat = timeofday + ttl`. `add_to_cache`'s `++incache > MAXCACHED` LRU evict walks `cachetop` to the tail (`while !(*cp).list_next.is_null()`) and `rem_cache`s it. `MyMalloc` declared returning `*mut c_char` to match dbuf.rs's decl (avoids the redeclared-signature warning), cast at each use.
  - **Classification.** Utility/leaf — not in `msgtab`. → **L1 only**: no client/server command path under the locked config (`NO_DNS_LOOKUP` on → the resolver is inert, the cache never populates at runtime). End-to-end DNS coverage arrives when the resolver proper is ported under a fixture nameserver.
  - **L1.** `ircd-testkit/tests/res_cache_build_diff.rs` — two fully-isolated `World`s of fn-pointers + global base pointers (`cachetop`/`hashtable`/`incache`/`cainfo`/`reinfo`/`timeofday`), copying `res_cache_lookup_diff.rs`: LIVE (`#[link_name]` → the Rust port over the res_link.o globals) vs CREF (`cref_*` over the renamed oracle). 8 tests with inverse/round-trip invariants — build-by-name T_A (entry contents + linkage + `ca_adds`/`incache` deltas + the steal inverse) and T_AAAA (flag), the short-ttl clamp (`ttl<600`→600 + `re_shortttl`) with the ≥600 inverse, dedup-by-name (T_A HIT → existing entry, no add) and dedup-by-addr (T_PTR HIT via `find_cache_number` → name merged as alias, no add), multi-addr contiguous-block build (distinct pointers, correct bytes), `add_to_cache` LRU boundary at MAXCACHED (incache pre-set near the cap; evict observed via `ca_dels` delta + list length) with the under-cap inverse, and both-bucket linkage (a single add is findable via both `find_cache_name` and `find_cache_number`). `timeofday` pinned to a fixed value per scenario so `expireat` is deterministic; process-global `LOCK` serializes the parallel `#[test]` threads. Zero diff. `make_cache`/`add_to_cache` confirmed `T` (Rust-defined) in `target/debug/ircd`.
  - **No L2/S2S.** Resolver inert under `NO_DNS_LOOKUP` (cache never populates at runtime); no `IsServer`/remote-field path. `golden_s_dns` (oper DNS stats / `DNS l` empty dump / non-oper 481) + `golden_registration` confirm the still-C resolver calls the Rust build/insert core without regression.

- **2026-06-05 — P6u (res.c request-queue management core) merged.** First bite into the `res.c` resolver proper — the only DNS file still in C after the P6q–P6t hostent-cache cluster — ported bottom-up from the outstanding-DNS-request queue floor.
  - **Cluster choice.** `add_request`/`rem_request`/`make_request`/`find_id`: the connected component over the file-statics `first`/`last` (the request queue heads) + `reinfo.re_requests`. No socket I/O, no answer parsing (none call `resend_query`/`query_name`/`do_query_*`/`proc_answer`), so it isolates cleanly as the floor the rest of the resolver builds on. The public queue walkers `del_queries`/`timeout_query_list` stay C this bite (`timeout_query_list` pulls in `resend_query` → the socket half).
  - **Guard / seam.** `res_link.o` gains `-DPORT_RES_REQ_P6u`. A new three-way `RES_REQ_LINKAGE` macro (parallel to `RES_CACHE_LINKAGE`): `extern` under `PORT_RES_REQ_P6u` (res_link.o — the four bodies `#ifdef`'d out, Rust defines them, the still-C callers `del_queries`/`timeout_query_list`/`gethost_*`/`do_query_*`/`get_res` resolve at link), blank under `RES_CACHE_EXPOSE` (cref oracle — GLOBAL so `cref_add_request`/`cref_rem_request`/`cref_make_request`/`cref_find_id` exist for L1), else `static`. The `first`/`last` queue heads flip from `static ResRQ *last, *first;` to `RES_DNS_STATIC` (already exposed in res_link.o via `PORT_DNS_M_DNS` + the cref oracle via `RES_CACHE_EXPOSE`) so the Rust port and the still-C walkers share one storage — exactly the P6r/P6t cache-globals pattern.
  - **Config-resolved body.** AFINET=AF_INET6 → `he.h_addrtype = AF_INET6`; DEBUG off → the `Debug(...)` traces in `rem_request`/`timeout_query_list` drop out; `MyFree` is a macro → `libc::free`; `MyMalloc`+`bzero` → `MyMalloc` + `ptr::write_bytes(…,0,…)`; the `bcopy(lp, &cinfo, sizeof(Link))` → `ptr::copy_nonoverlapping` (or a zero-fill when `lp==NULL`).
  - **Faithfulness notes.** `rem_request`'s `for (rptr=&first; *rptr; r2ptr=*rptr, rptr=&(*rptr)->next)` rendered as a raw `*mut *mut reslist` walk advancing `r2ptr`/`rptr` at the bottom so the `last==old → last=r2ptr` tail fix-up matches; the post-loop free order (`he.h_name`, the `MAXALIASES` `h_aliases[]`, `name`, the node) preserved. `make_request` sets `next=NULL` before `add_request` (which sets it again) verbatim. `add_request(NULL)` returns -1 (the C no-op) before touching the queue.
  - **Classification.** Utility/callees — neither in `msgtab`. → **L1 only, NO L2, NO S2S.** The resolver is inert under `NO_DNS_LOOKUP` (no client/server command path populates the queue at runtime; end-to-end queue coverage arrives under a fixture nameserver when the socket/answer half lands).
  - **L1.** `res_request_diff` — two fully-isolated `World`s of fn-pointers + `first`/`last`/`reinfo` base pointers (LIVE `#[link_name]` → the Rust port over the res_link.o globals; CREF `cref_*` over `cref_first`/`cref_last`/`cref_reinfo`). Positive + inverse/round-trip: `make_request` seeds every default field & appends (NULL `lp` zeroes `cinfo`); `add_request` arrival ordering + `re_requests` increments, and `add_request(NULL)` → -1 with the queue untouched; `find_id` hit (first/middle/tail) / miss / misses-after-its-target-is-`rem_request`'d; `rem_request` head/middle/tail unlink with `last` repointed on tail removal + the node freed; a drained queue is `first==last==NULL` and reusable by a fresh `make_request`. `timeofday` pinned (deterministic `sentat`); a process-global `LOCK` serializes the parallel `#[test]` threads over the shared queue. Zero diff over 8 tests.
  - **L2/S2S.** None (see Classification). `cargo test -p ircd-golden` `golden_s_dns` (2) + `golden_registration` (2) green — the still-C resolver `init_resolver`/walkers call the Rust queue primitives at boot without regression. 0 warnings.
  - **Still C in `res.c`.** `del_queries`/`timeout_query_list` (the public queue walkers) + the resolver socket/answer half: `send_res_msg`, `do_query_name`/`do_query_number`, `query_name`, `resend_query`, `proc_answer`, `get_res`, `gethost_byname`/`gethost_byname_type`/`gethost_byaddr`, `init_resolver`, `cres_mem`, `bad_hostname`.

- **2026‑06‑05 — P6v (`res.c` queue walkers `del_queries`/`timeout_query_list`) merged.** The second bite of the `res.c` resolver proper, built on the P6u queue core — the reaper/timeout layer that walks the outstanding-DNS request queue (`first`/`last`).
  - **Cluster choice.** Continuing bottom-up after the P6u request-queue floor: `del_queries`/`timeout_query_list` are the two queue *walkers* the PLAN names as the next nameable unit. `del_queries` deps are purely Rust (`rem_request` P6u) + the shared `first` global → fully portable. `timeout_query_list` additionally calls the still-C `resend_query` (the resend/socket path) — the one inverse-direction dependency.
  - **Guard / seam.** `res_link.o` gains `-DPORT_RES_WALK_P6v` (build.rs); `res.c` wraps both bodies in `#ifndef PORT_RES_WALK_P6v`. New `RES_RESEND_LINKAGE` macro: `extern` under `PORT_RES_WALK_P6v` (res_link.o exposes `resend_query` as a global so the Rust `timeout_query_list` links against it), `static` otherwise — the cref oracle keeps `resend_query` static, and `cref_timeout_query_list` resolves to the objcopy-renamed local `cref_resend_query` inside libcref.a (self-contained: `resend_query` has no out-of-TU caller). Both `del_queries`/`timeout_query_list` are already public (res_ext.h) → the still-C callers (`s_bsd.c:1361/3288`, `ircd.c:580/1193`) and the Rust `list.rs:free_conf` resolve to the Rust defs at link.
  - **Config‑resolved body.** DEBUG off → the `Debug(())`/`myctime` traces drop out of both functions. USE_IAUTH on → the ASYNC_CLIENT exhausted arm calls `sendto_iauth("%d d", cptr->fd)`. `ClearDNS(x)` = `(*x).flags &= ~FLAGS_DOINGDNS` (0x100; `flags` is c_long). The `cinfo.flags` switch: ASYNC_CLIENT=0 (iauth+ClearDNS), ASYNC_CONNECT=1 (sendto_flag SCH_ERROR), default (ASYNC_CONF=2 etc.) → reap only. **Platform note:** `reslist.retries`/`resend` render as `c_char` = **u8** here, so `--rptr->retries` is ported as `wrapping_sub(1)` (faithful to C's modular char; the `<= 0` test then means `== 0`, matching unsigned C on the same platform) — panic-free even though the live values (1‑3) never underflow.
  - **Faithfulness.** `timeout_query_list` mirrors the C `for (rptr=first; rptr; rptr=r2ptr)` walk with `r2ptr` captured *before* `rem_request` frees the node; the exhausted arm uses `continue` (skipping the `next` update) exactly as C; `next` seeded 0 with the `!next || tout < next` min; return `(next > now) ? next : now + AR_TTL`. `resend_query` short-circuits on `resend == 0`, so the L1 retry test exercises the call with no socket I/O.
  - **Classification.** Utility/callees — neither is in `msgtab` (callers: s_bsd.c registration/cleanup, ircd.c io_loop/exit, list.rs free_conf). **L1 only.**
  - **L1.** `ircd-testkit/tests/res_walk_diff.rs` (modeled on `res_request_diff.rs`): two fully-isolated `World`s (LIVE `#[link_name]` → the Rust port over res_link.o globals; CREF `cref_*` over the renamed oracle), each carrying base pointers to `first`/`last`/`reinfo` + the four fn-pointers. 9 tests, positive + inverse: `del_queries` head/middle/tail match, absent-cp no-op, all-match drain (`first==last==NULL`); `timeout_query_list` future-dated survive (returns min `tout`), exhausted ASYNC_CONF (no-notify reap → `now+AR_TTL`), exhausted ASYNC_CLIENT (ClearDNS clears the DNS bit but leaves an unrelated bit, diffed on two per-world clients), exhausted ASYNC_CONNECT, retry back-off (`retries--`, `sentat=now`, `timeout` 4→8, `resend_query` no-op via resend=0), and a mixed survivor+reaped+retried walk — return value, surviving ids, per-node `retries`/`sentat`/`timeout`, and `re_timeouts` all diffed. `timeofday` pinned; `adfd`/`cref_adfd=-1` so `sendto_iauth` no-ops; `svchans` BSS-zero so `sendto_flag` no-ops; process `LOCK` serializes the parallel `#[test]` threads. **Zero diff.**
  - **No L2/S2S.** The resolver is inert under `NO_DNS_LOOKUP` — the request queue never populates at runtime, so there's no client/server command path that reaches these walkers. `golden_s_dns` (2) + `golden_registration` (2) stay green, confirming the still-C resolver calls the Rust walkers without boot regression. End-to-end DNS coverage arrives when the answer parser + socket I/O half is ported under a fixture nameserver.
  - **Still C in `res.c`:** only the answer parser + socket I/O half — `send_res_msg`/`do_query_name`/`do_query_number`/`query_name`/`resend_query`/`proc_answer`/`get_res`/`gethost_byname`/`gethost_byname_type`/`gethost_byaddr`/`init_resolver`/`cres_mem`/`bad_hostname`.

- **2026-06-05 — P6w (`res.c` `bad_hostname`) merged.** The next bottom-up leaf of the still-C `res.c` resolver proper.
  - **Cluster choice** — `bad_hostname` (res.c:1832), the pure hostname-character validator used by the answer parser `proc_answer` (res.c:802,851) to reject malformed PTR/A results. No socket, no globals, no allocation — the cleanest remaining `res.c` leaf to extract before the socket/answer-parser core. Single function → atomic cluster.
  - **Config-resolved body** — `RESTRICT_HOSTNAMES` is defined (cbuild/config.h:608), `HOSTNAMES_UNDERSCORE` is not. So the compiled body is the restrictive branch *without* the underscore escape hatch: `isalnum` ok; interior hyphen ok unless leading / after-dot / trailing (`len==1`) / before-dot; `.` ok unless leading or doubled; else `-1`.
  - **Guard / seam** — upstream file-static, so a new three-way `RES_HOSTNAME_LINKAGE` (mirroring P6q/P6r/P6s `RES_CACHE_LINKAGE`): `extern` when building res_link.o (`-DPORT_RES_HOSTNAME_P6w`, body `#ifndef`'d out → Rust def wins, still-C `proc_answer` resolves at link), GLOBAL in the cref oracle (`-DRES_CACHE_EXPOSE` → `cref_bad_hostname` for L1), `static` in the plain build. Applied to the forward decl (res.c:132) + the definition. `build.rs` appends `-DPORT_RES_HOSTNAME_P6w` to the res_link.o compile line.
  - **Faithfulness notes** — the C `for (s=name; (c=*s) && len; s++, len--)` runs the `s++, len--` increment on every `continue` (C `continue` jumps to the increment) and never falls through to it (each path `continue`s or `return -1`s); the Rust port advances `s`/`len` *before* each `continue` and skips it on the `-1` exit. `isalnum(c)` is called as `libc::isalnum(c as c_int)` with the same (signed-char-promoted) value → byte-identical shared-libc table lookup. `s[1]` is read only when `len != 1` — the Rust `&&` short-circuits identically, so `*s.add(1)` is never evaluated at the cap.
  - **Classification** — utility/callee TU (not in `msgtab`; called only from the still-C `proc_answer`). **L1 is the gate.** No client command reaches it under the locked config without the still-C answer parser running.
  - **L1** — `ircd-testkit/tests/res_bad_hostname_diff.rs`: declare `c_bad_hostname` (`#[link_name="bad_hostname"]` → Rust def via res_link.o) + `cref_bad_hostname` (oracle), drive both over a corpus × every prefix length `0..=len+1` (the `len` cap is load-bearing — only the first `len` bytes are scanned) and assert identical return. Covers valid names (incl. punycode interior double hyphen), hyphen edge cases (leading/trailing/after-dot/before-dot + the `len==1` trailing-hyphen guard), dot edge cases (leading/trailing/doubled), illegal chars (space, underscore, `/`, `*`, `:`, control `\x07`, high-bit), and the `len` boundary (illegal char at `len` vs `len-1`, `len==0`, empty string). 6 tests, zero diff.
  - **S2S** — no path (the function is unreachable without the still-C answer parser; no `IsServer`/remote-field/propagation path exists).

- **2026-06-05 — P6x (`res.c` `cres_mem`) merged.** The DNS-cache memory-usage reporter — the next bottom-up leaf of the `res.c` resolver proper, ported to `ircd-common/src/res.rs`.
  - **Cluster choice** — `cres_mem(aClient *sptr, char *nick)` (res.c:1810): walks the hostent cache list (`cachetop`, via `list_next`) summing each `aCache`/`hostent`'s struct bytes (`sm`), IP storage (`im` — `sizeof(char*)+sizeof(struct IN_ADDR)` per addr, +1 for the NULL terminator) and name storage (`nm` — `sizeof(char*)+strlen` per alias, then the `i-1`/`+sizeof(char*)` tail, then `strlen(h_name)`); `ts = ARES_CACSIZE(1009) * sizeof(CacheTable)(16) = 16144`; sends two RPL_STATSDEBUG (249) lines and returns `ts+sm+im+nm`.
  - **Seam / Guard** — `cres_mem` is **non-static**, exported in `res_ext.h`, so the cref oracle already carries `cref_cres_mem` → **no new `RES_*_LINKAGE`/`*_EXPOSE` seam** (contrast the file-static cache/queue leaves P6q–P6w). `#ifndef PORT_RES_CRES_MEM_P6x` wraps the C body; `ircd-sys/build.rs` adds `-DPORT_RES_CRES_MEM_P6x` to the `res_link.o` compile so the body is dropped and the Rust `#[no_mangle] cres_mem` resolves it for `s_debug.c`'s `count_memory` at link. The cref oracle `res.o` is untouched.
  - **Config-resolved body** — DEBUG off; the whole body compiles unconditionally. `me` (bindgen `static mut me`) read for the server name; `sendto_one` via the existing res.rs variadic extern.
  - **Faithfulness notes** — the `nm += i - 1` step is **unsigned modular** arithmetic: with 0 aliases `i == 0`, so it adds `(int)-1` widened to `u_long` (= `u_long::MAX`, i.e. `nm -= 1`), and the following `nm += sizeof(char*)` nets `+7`. Reproduced with `wrapping_add` throughout the `nm` accumulation so it may transit `u_long::MAX` without a debug-mode overflow panic. The `%d` format applied to the `u_long` args (`ts`/`sm`/`im`/`nm`) is preserved verbatim (reads the low 32 bits) — the values pass as `u_long` so the live trampoline and `cref_sendto_one` push identical bytes.
  - **Classification** — utility/callee (not in `msgtab`); reached from `count_memory` via `STATS z`. Read-only on the cache.
  - **L1** (`ircd-testkit/tests/res_cres_mem_diff.rs`) — two-world (LIVE over `cachetop`/`me`/the Rust `cres_mem`; CREF over `cref_cachetop`/`cref_me`/`cref_cres_mem`) build of identical caches + a buffering local client (`make_client`, P2) per world with a pinned `me.name`. Diffs **both** the return value and the **sendQ bytes** (the two RPL lines). Cases: empty cache (return == `ts` = 16144, `Structs 0 IP storage 0 Name storage 0`); single entry / **0 aliases** (the `nm += i-1` wrap → `+7`); single entry / 3 aliases / 2 addrs; NULL `h_name` (trailing `strlen` skipped); 3 entries (`sm` scales per node). Hand-computed totals match the cref oracle. Zero diff.
  - **L2** — already covered by `golden_debug::stats_memory_match_reference` (STATS z → `count_memory` → `cres_mem`); stays green (empty boot cache under NO_DNS_LOOKUP → `ts`-only walk).
  - **S2S** — none. No `IsServer`/remote-field path (oper STATS only).
  - **Still C in res.c** — the socket/answer-parser core only: `send_res_msg`/`do_query_*`/`query_name`/`resend_query`/`proc_answer`/`get_res`/`gethost_*`/`init_resolver`.

- **2026-06-05 — P6y (`res.c` send_res_msg) merged.** The resolver UDP datagram-send leaf — the literal bottom of the send tree (`do_query_name`/`do_query_number`/`resend_query` → `query_name` → `send_res_msg`). Continues the bottom-up `res.c` resolver-proper port; after this only the answer-parser + socket-I/O core remains C (`query_name`/`do_query_*`/`resend_query`/`proc_answer`/`get_res`/`gethost_*`/`init_resolver`).
  - **Seam.** `send_res_msg` is upstream file-static → new three-way `RES_SEND_LINKAGE` (same shape as `RES_RESEND_LINKAGE`/`RES_HOSTNAME_LINKAGE`): `extern` in res_link.o (`-DPORT_RES_SEND_P6y`; body `#ifndef`-dropped, Rust defines it, the still-C `query_name` resolves at link), GLOBAL in the cref oracle (`-DRES_CACHE_EXPOSE` → `cref_send_res_msg` for L1), else `static`. The resolver socket fd `resfd` it sends on is a real exported global (`s_bsd_ext.h`) — declared extern in res.rs, no extra seam; `ircd_res` is the Rust res_init.rs global (P6p).
  - **Config-resolved body / faithfulness.** `max = MIN(nscount, rcount)`, overridden to 1 on `RES_PRIMARY` (=16), then floored to 1 — the clamp order is load-bearing (`rcount=0` → 1 send; `RES_PRIMARY` with nscount=3 → 1 send). Each iteration force-sets `nsaddr_list[i].sin6_family = AF_INET6` (=AFINET) then `sendto(resfd, msg, len, 0, &nsaddr_list[i], sizeof(sockaddr_in6))`; `reinfo.re_sent`/`sent` bump **only** on a full-length send (`== len`); the DEBUGMODE-off failure branch is a no-op. Returns `sent`, or -1 on NULL msg / all-failed.
  - **Classification.** Utility/callee — not in `msgtab`; only caller is `query_name` (res.c). No client command reaches it under the locked config (`NO_DNS_LOOKUP` disables the DNS path).
  - **L1.** `ircd-testkit/tests/res_send_msg_diff.rs`: two fully isolated `World`s (LIVE via `#[link_name]` → Rust over res_link.o/res_init globals; CREF via `cref_*`). Each world+scenario binds its own loopback (`::1`) UDP recv socket(s) with a 300ms `SO_RCVTIMEO` + its own send socket, points `ircd_res.nsaddr_list[]` at the recv port(s), zeroes `reinfo.re_sent`, drives `send_res_msg`, then `recvfrom`s and snapshots `{return value, datagram bytes received, re_sent delta, nsaddr_list[0].sin6_family}`. Six cases diff byte-identical: single-nameserver one-send; NULL msg → -1/no send; `rcount=0` floor-to-1; `RES_PRIMARY` (nscount=2) override-to-1 (only ns[0] receives); two-nameserver both-receive; bad `resfd` (-1) → all sends fail → -1, counter untouched.
  - **L2/S2S.** None. No DNS command path under `NO_DNS_LOOKUP`; boot/registration (`golden_registration`) and oper-DNS (`golden_s_dns`) goldens stay byte-identical, confirming the dropped C body doesn't perturb the binary. No `IsServer`/remote-field path.

- **2026-06-05 — P6z (`res.c` `query_name` — the DNS query builder/sender) merged.** The next bottom-up leaf of the resolver proper, sitting directly above the P6y `send_res_msg` leaf in the send tree. Ported to `ircd-common/res.rs` via the `res_link.o` partial port (`-DPORT_RES_QUERY_P6z`).
  - **Cluster choice** — `static int query_name(char *name, int class, int type, ResRQ *rptr)` (res.c:618). Its only callees are already Rust: `ircd_res_mkquery` (P6o), `find_id` (P6u), `send_res_msg` (P6y). Its callers are still C: `do_query_name`/`do_query_number`/`resend_query`/`get_res`. So it is the next clean leaf up the tree.
  - **Linkage / seam** — upstream file-static → new three-way `RES_QUERY_LINKAGE` (mirrors `RES_SEND_LINKAGE`): `extern` under `-DPORT_RES_QUERY_P6z` (res_link.o body `#ifdef`'d out → Rust def; still-C `do_query_*`/`get_res` resolve at link), GLOBAL under `-DRES_CACHE_EXPOSE` (cref oracle → `cref_query_name` for L1), static otherwise. No new data-global seam — it touches only `ircd_res`/`resfd`/`first`/`reinfo`, all already exposed (P6p/P6u/P6y). (Gotcha hit while writing the C comment: `do_query_*/` contains a literal `*/` that closes the block comment early → reworded to `do_query_name/do_query_number`.)
  - **Config-resolved body** — `LRAND48` absent from `cbuild/setup.h`/`config.h` → the id source is `gettimeofday(&tv,NULL)` + `tv.tv_usec & 0xffff` (the `lrand48()` branch is dead); `DEBUG` off → no `Debug()` trace. `MAXPACKET=1024`, `QUERY=0`, `NO_RECOVERY=3`, `TRY_AGAIN=2`. `reslist.sends` is a signed `char` → `sends++` is an `i8` `wrapping_add`, sign-extended to `int` as `send_res_msg`'s `rcount`. `hptr->id` is the HEADER 16-bit bitfield → `(*hp).id()`/`set_id` (stored big-endian; `ntohs`=`u16::from_be`), the res_mkquery.rs:84 pattern. The do/while collision loop is a faithful `loop { …; k += 1; if find_id(...).is_null() { break } }`.
  - **Classification** — utility/callee leaf, **not in `msgtab`**; reachable only through the still-C resolver entry points, inert project-wide under `NO_DNS_LOOKUP`. **L1 is the gate.**
  - **L1** — `ircd-testkit/tests/res_query_name_diff.rs`: two fully-isolated `World`s (LIVE = Rust port over res_link.o/res_init globals; CREF = `cref_*` oracle), each with its own loopback `::1` UDP recv+send sockets + `ircd_res`/`reinfo`/`resfd`/`first` base pointers (the `res_send_msg_diff.rs` shape). **Determinism via a strong interposed `#[no_mangle] gettimeofday`** returning a fixed `tv` — both worlds (the Rust `libc::gettimeofday` call and the cref-archive call) resolve to it at link, so packets are byte-identical **including the id field** and the `find_id` collision loop is reproducible (`ircd_res.id` pinned to 0 → mkquery writes `htons(1)`, first computed id = `1 + (tv_usec & 0xffff)`). Diff on `{ret, datagram bytes, rptr.{id,sends,sent}, re_sent delta, h_errno}` over: A-record query (1 send, id == header id, sends 0→1), PTR query (qtype trailer == T_PTR), a forced one-collision id advance (seed the queue with a node whose id == the first computed id) vs the no-collision inverse (k stays 0 → id == first computed) with the exact second-iteration id checked against the C formula, mkquery-failure (300-char single label → dn_comp fails → `r<=0`, NO_RECOVERY, no send, `sends`/`sent`/`re_sent` untouched), and send-failure (`resfd=-1` → `send_res_msg` -1 → TRY_AGAIN, but `sends` **still** bumped before the send, `sent`/`re_sent` untouched). Zero diff over 5 tests.
  - **L2 / S2S** — none. The DNS path is disabled project-wide via `NO_DNS_LOOKUP`; the boot goldens `golden_registration` + `golden_s_dns` stay byte-identical, confirming the still-C resolver calls the Rust `query_name` without regression. End-to-end DNS coverage arrives when the resolver proper (answer parser + socket I/O) is ported under a fixture nameserver.

- **2026-06-05 — P6aa (`res.c` query-dispatch layer `do_query_name`/`do_query_number`) merged.** The next bottom-up cluster of the still-C `res.c` resolver proper, sitting directly above the P6z `query_name` leaf and below the still-C `gethost_*`/`get_res`/`resend_query` callers.
  - **Cluster choice.** `do_query_name` (res.c:541) builds the forward-lookup hostname and `do_query_number` (res.c:580) builds the reverse-lookup name; both allocate the `ResRQ` via `make_request` (P6u) when none was passed and then call `query_name` (P6z) — all three callees are already Rust, so this is the clean next leaf up the send tree. `resend_query` (which dispatches to both) is the next unit *above* and stays C this bite (now calling the Rust do_query_*).
  - **Guard / seam.** `res_link.o` gains `-DPORT_RES_DOQUERY_P6aa`. Both functions are upstream file-static → a new three-way `RES_DOQUERY_LINKAGE` (mirrors `RES_QUERY_LINKAGE`): `extern` under `PORT_RES_DOQUERY_P6aa` (res_link.o — the two bodies `#ifndef`'d out, Rust defines them, the still-C callers `gethost_byname_type`/`gethost_byaddr`/`get_res`/`resend_query` resolve at link), GLOBAL under `RES_CACHE_EXPOSE` (cref oracle — `cref_do_query_name`/`cref_do_query_number` for L1), else `static`. No new data-global seam — they touch only `ircd_res`/`first`/`last`/`reinfo`/`resfd`, all already exposed (P6p/P6u/P6y). (Gotcha re-hit from P6z: a `gethost_*/` in the macro comment closes the block comment early via the literal `*/` — reworded to spell out the caller names.)
  - **Config-resolved body.** `AFINET=AF_INET6` → `rptr->addr`/`he.h_addr` are `in6_addr` (16 bytes); `IN6ADDRSZ` = `sizeof(struct IN_ADDR)` = 16. `RES_DEFNAMES = 0x80` (resolv_def.h). DEBUG off → the `Debug((…))` trace drops out. `strncpyzt(x,y,N)` = `strncpy(x,y,N); x[N-1]=0` (struct_def.h:871); `HOSTLEN=63` → `hname[HOSTLEN+1]` = `[c_char; 64]`. `index`=`strchr`; `MyMalloc` returns `*mut c_char`.
  - **Faithfulness notes.** The domain-append branch is gated on `rptr != NULL` (the resend path) in the C — faithful: a *fresh* `do_query_name` (rptr==NULL) never appends `defdname`, and stores the **original** (un-truncated/un-appended) `name`, not `hname`. `do_query_number`'s two `sprintf` branches are reproduced by building the exact byte string in Rust (`{}` decimal for the IPv4 form, single-nibble lowercase `{:x}` for the ip6.arpa form, byte-reversed low-then-high nibble) and `copy_nonoverlapping` into the C `ipbuf`; `bcopy` → `copy_nonoverlapping`; `&rptr->he.h_addr` = `&he.h_addr_list[0]`.
  - **Classification.** Utility/callee leaf — neither in `msgtab`; reachable only through the still-C resolver entry points, inert project-wide under `NO_DNS_LOOKUP`. → **L1 only, NO L2, NO S2S** (no `IsServer`/remote-field path).
  - **L1.** `ircd-testkit/tests/res_do_query_diff.rs` — two fully-isolated `World`s (LIVE `#[link_name]` → the Rust port over the res_link.o/res_init globals; CREF `cref_*` over the renamed oracle), each with its own loopback `::1` UDP recv+send sockets + `ircd_res`/`reinfo`/`resfd`/`first`/`last` base pointers (the `res_query_name_diff.rs` shape). A strong interposed `gettimeofday` pins `query_name`'s id randomization so packets are byte-identical including the id. Diffs `{ret, datagram bytes, the resulting ResRQ (type/name/id/sends/sent), he.h_addr bytes, he.h_length, addr bytes, re_sent delta, h_errno}` over 7 tests: do_query_name fresh (original name stored verbatim, no append, 1 send); do_query_name resend + RES_DEFNAMES (`.example.org` appended → longer packet) vs the already-dotted inverse (no append); the overlong-defdname overflow → -1/no send; do_query_number IPv4-mapped (`::ffff:127.0.0.1` → `in-addr.arpa`, `he.h_addr`/`addr`/`he.h_length=16` seeded) + the all-zero-prefix arm + the full-IPv6 `ip6.arpa` nibble form; and do_query_number resend (no make_request, he/addr untouched, queue stays empty). Zero diff. `do_query_name`/`do_query_number` confirmed `T` (Rust-defined) in `target/debug/ircd`.
  - **L2/S2S.** None (see Classification). `cargo test -p ircd-golden` `golden_s_dns` (2) + `golden_registration` (2) stay byte-identical — the still-C `gethost_*`/`get_res`/`resend_query` call the Rust do_query_* without boot regression. 0 warnings.
  - **Still C in `res.c`.** Only the answer parser + remaining socket I/O: `resend_query`/`proc_answer`/`get_res`/`gethost_byname`/`gethost_byname_type`/`gethost_byaddr`/`init_resolver`.

- **2026-06-05 — P6bb (`res.c` `gethost_byname_type`/`gethost_byname`/`gethost_byaddr`) merged.** The public host-lookup entry points — the next bottom-up cluster of the resolver proper, sitting directly above the Rust `find_cache_*` (P6s) + `do_query_*` (P6aa) and below the still-C `get_res`/`s_bsd` callers.
  - **Cluster choice.** The three exported lookup fns are thin dispatchers: bump a counter → try the cache → on a miss issue an async query (when a client `lp` is present) or give up. They depend only on already-ported Rust (`find_cache_name`/`find_cache_number`, `do_query_name`/`do_query_number`), so they are the clean next leaf above the do_query layer.
  - **Mechanism / no seam.** Ported into `ircd-common/res.rs`; `res_link.o` drops the C bodies via `-DPORT_RES_GETHOST_P6bb`. Because all three are exported `res_ext.h` symbols (not file-statics), **no linkage seam is needed** — the cref oracle's unguarded `res.o` already exports `cref_gethost_*` for L1 (same pattern as P6x `cres_mem`).
  - **Config-resolved body.** `DEBUG` off → the `Debug(...)` trace in the miss path vanishes. `gethost_byname` delegates with `T_AAAA` (AFINET=AF_INET6 → IPv6-first; `get_res` falls back to `T_A`). `gethost_byname_type` rejects any non-`T_A`/`T_AAAA` type with NULL **after** bumping `re_na_look`; the cache flag is `FLG_AAAA_VALID`/`FLG_A_VALID` by type. `gethost_byaddr` gates the number cache on `FLG_PTR_VALID` (via `find_cache_number`). `(struct hostent *)&(cp->he)` → `addr_of_mut!((*cp).he)` (already `*mut hostent`, no cast).
  - **Faithfulness.** Counter bump precedes the type guard; cache consulted before any query; `lp == NULL` short-circuits to NULL before `do_query_*`; the async query path always returns NULL (the result arrives later via `get_res`).
  - **Compiled-out caller.** `gethost_byaddr`'s sole C caller (`s_bsd.c:1681`) lives in the `#else` of `#ifdef NO_DNS_LOOKUP`; the project builds with `NO_DNS_LOOKUP`, so the call is compiled out and the linker omits the (unreferenced) Rust fn from the `ircd` binary. It is still ported faithfully and L1-verified — `nm` confirms `gethost_byname`/`gethost_byname_type` resolve to Rust (`T`) in the binary; `gethost_byaddr` simply has no caller to pull it in.
  - **Classification.** Utility/leaf — none in `msgtab` → **L1 only**. No `IsServer`/remote-field path; the resolver is inert under `NO_DNS_LOOKUP`, so no client/server command reaches these at runtime.
  - **L1.** `ircd-testkit/tests/res_gethost_diff.rs` (8 cases, zero diff). Two fully isolated worlds (live `#[link_name]` Rust port vs `cref_*` oracle) spanning **both** the DNS cache globals (`cachetop`/`hashtable`/`cainfo`) and the resolver query globals (`ircd_res`/`reinfo`/`resfd`/`first`/`last`), each binding its own loopback `::1` UDP recv socket + a pinned `gettimeofday` (deterministic query id, matching res_do_query_diff). Branch matrix + inverses: bad-type early-out (re_na_look bumped, cache untouched, no query), cache HIT (returns `&he`, `ca_na_hits`/`ca_nu_hits` bumped, no datagram) vs MISS, `lp==NULL` short-circuit vs `lp!=NULL` async query (AAAA datagram + queued `T_AAAA` node for byname; PTR/`ip6.arpa` datagram + queued `T_PTR` node for byaddr), the byname→byname_type(T_AAAA) delegation, and the `re_na_look` (byname) vs `re_nu_look` (byaddr) split.
  - **S2S.** None — no server-reachable path (see Classification).
