  - **`format!` per user request — N/A, documented in‑module.** m_njoin builds no ASCII reply of its own; the `uidbuf` NJOIN‑relay buffer (the re‑emitted UID member list) and the `modebuf`/`parabuf` synthetic‑MODE scratch are assembled byte‑for‑byte (arbitrary client UID/nick bytes — a `String` would misframe non‑ASCII); every wire line goes to a C variadic sender.
  - **Faithfulness notes.** The mode‑prefix parse (`@`/`@@`/`@@+`/`@+`/`+` → `mbuf` `o`/`ov`/`v` + `chop` CHFL bits, `name` advanced past the prefix, `target` left at the prefix for the verbatim uidbuf copy) is branch‑for‑branch. The MODE‑burst accumulator (the `cnt` switch): case 0 falls through to case 1 (`strcat modebuf/parabuf`, the `mbuf[1]` double‑name for a `+ov` user), case 2 flushes `:%s MODE %s +%s%c %s %s` (the `mbuf[0]` `%c` promoted `as c_int`), `cnt == MAXMODEPARAMS` flushes `:%s MODE %s +%s %s` and resets — modelled as `if cnt==0||cnt==1 {…} else if cnt==2 {…}` (the C fall‑through). The size_t/ptrdiff math (`maxlen = BUFSIZE - 17 - strlen(parv0) - strlen(parv1) - NICKLEN`; the `u.offset_from(u_base) >= maxlen` flush test) mirrored as signed `isize`. Empty‑channel lock (`parv[2]=="."` → `history = timeofday + (L)DELAYCHASETIMELIMIT`, `istat.is_hchan*` bumps, return 0) ported (no observable wire → review‑covered). JOIN relay `:%s JOIN %s%s` with the `:`‑prefix‑unless‑bursting netjoin‑discriminator trick. Already‑on‑channel error class (`IsBursting` → SCH_ERROR notice + `ERROR` line to cptr; else SCH_CHAN "Fake:" notice). Source SID = `sptr->serv->sid` (the NJOIN origin server). Returns 0.
  - **L2‑S2S green (the only gate).** New `golden_s2s_njoin`: the `@+` (op+voice) MODE‑burst path the existing `golden_s2s_membership` (plain add/remove) doesn't cover — a peer NJOINs carol `@+001BBBBBB` onto a `#chan` a LOCAL member (alice) is already on → alice observes the relayed `:carol… JOIN :#chan` + the synthetic `:peer.test MODE #chan +ov carol carol` burst (exercising the `mbuf[1]` double‑param path). `golden_s2s_membership` (remote NJOIN add → `add_user_to_channel`, remote PART remove, the deop/clean‑readd inverse) now drives the **Rust** m_njoin as a regression; the other NJOIN‑driving goldens (`golden_s2s_join`/`golden_s2s_kick`/`golden_s2s_topic`) stay byte‑identical ref‑C == Rust. `cargo build` 0 warnings; no new canonicalizer masks. **Remaining channel.c (C):** the mode cluster (`m_mode`/`set_mode`/`add_modeid`/`del_modeid`/`send_mode_list`/`make_bei`/`free_bei`/`send_channel_*`), `reop_channel`/`collect_channel_garbage`/`setup_server_channels`, `free_channel`, and `check_string` stay in `channel_link.o`.
- **2026‑06‑03 — P5ff (channel.c mode‑id list layer) merged.** `check_string` (channel.c:176), `make_bei` (:212), `free_bei` (:193), `add_modeid` (:263), `del_modeid` (:331) — the ban/exception/invite (`+b`/`+e`/`+I`/`+R`) mode‑id list‑item allocators + the `chptr->mlist` add/del — ported to `ircd-common/src/channel.rs`, extending the existing `channel_link.o` partial port (`‑DPORT_CHANNEL_P5`). **Thirteenth channel.c sub‑phase**; the connected component over the file‑static `asterix[2]="*"` var ({check_string, make_bei, free_bei} reference it; add_modeid/del_modeid call make_bei/free_bei). Plan: `docs/superpowers/plans/2026-06-03-p5ff-channel-modeid-list.md`.
  - **All five are file‑`static` in C → the P5u/P5x extern‑prototype switch on each.** The still‑C `set_mode` calls `check_string` (:1295/:1400), `make_bei` (:1786), `add_modeid` (:1934), `del_modeid` (:1937); `set_mode` + the still‑C `free_channel` (:2368) call `free_bei`. So each `static` forward decl flips to `extern` under `#ifdef PORT_CHANNEL_P5` and the body is guarded out, resolving the C remnant to the Rust `#[no_mangle]` def at link (linkage‑only, faithful). `add_modeid`/`del_modeid`/`free_bei`/`make_bei` had forward decls (channel.c:97/98/112/113); `check_string` had **none** (defined before its callers) → a new `extern char *check_string(char *)` added under the guard (cf. P5x `add_user_to_channel`). RED confirmed via exactly the five undefined symbols (`asterix` has no external C reference → no link error, as predicted).
  - **`asterix` is module‑private (`static mut ASTERIX: [c_char;2]`).** C never references `asterix` outside the now‑ported `check_string`/`make_bei`/`free_bei` (verified: channel.c:44/181/190/195/199/203/220/222/231/233/242/244 are all inside the cluster), so the Rust pointer identity is self‑consistent: `make_bei` stores the Rust asterix ptr, `free_bei` compares against it (`!= asterix_ptr()` ⇒ a MyMalloc'd component to `free`), and the still‑C `set_mode`/`free_channel` only ever *pass* the resulting `aListItem` (never compare a component to their own asterix). No cross‑language pointer‑identity mismatch. The C `asterix` def is `#ifndef`‑guarded out.
  - **Callees, all already resolved:** Rust — `collapse` (P1 match_; the `add_modeid` MyClient nick/user/host collapse), `mycmp` (P1; `BanExact`), `make_link`/`free_link` (P2 list), `istat` (P2; `is_bans`/`is_banmem` are `u_long` → `wrapping_add`/`wrapping_sub`, cf. P5u add_invite — a debug `-=` underflow would abort the nounwind extern fn); libc — `MyMalloc`(P2)/`free`/`strncpy`(`strncpyzt_` helper)/`strlen`/`isspace` (added to the extern block; `check_string` passes the char sign‑extended `as c_int` as C does). New imports: `aListItem`/`collapse`/`ERR_BANLISTFULL`/`HOSTLEN`/`MAXBANLENGTH`/`MAXBANS`/`RPL_BANLIST`/`RPL_EXCEPTLIST`/`RPL_INVITELIST`/`RPL_REOPLIST`/`USERLEN`. Inline `ban_len`/`ban_exact` macros + `asterix_ptr`/`strncpyzt_` helpers. The `add_modeid` `(len > MAXBANLENGTH) || (++cnt >= MAXBANS)` short‑circuit modelled as `len > … || { cnt += 1; cnt >= … }` (the `++cnt` block runs only when the left is false — faithful to C's `||`). `bzero(mode, sizeof(Link))` → `ptr::write_bytes(mode,0,1)`.
  - **No L1 — all five are file‑`static` → the `cref_` archive keeps them local (no `cref_add_modeid` oracle, cf. P5v `match_modeid` / P5dd `can_join`).** The data‑op faithfulness is gated at **L2**: ref‑C `set_mode` calls C `add_modeid`/`make_bei`; the Rust build's (still‑C) `set_mode` calls the **Rust** ones — byte‑identical wire output through `set_mode` is the gate.
  - **`format!` per user request — N/A, documented in‑module.** `make_bei` copies arbitrary client‑supplied ban‑mask bytes (`nick`/`user`/`host`, capped NICKLEN/USERLEN/HOSTLEN) via `strncpyzt_` byte copies (a UTF‑8 `String` would corrupt non‑ASCII — the standing P5c/P5f rule); every reject reply (`ERR_BANLISTFULL`, `RPL_BANLIST`/etc.) goes straight to the C variadic `sendto_one`; no sub‑`long` int reaches a sender.
  - **L2 green + L2‑S2S green (the gates).** New `golden_channel_ban` (L2): local chanop alice `+b foo!bar@baz`/`+e *!*@trusted.example`/`+I friend!*@*` add (→ `make_bei` 3‑component split + asterix‑fill + `add_modeid` head‑prepend), the `MODE #chan b`/`e`/`I` query (`367`/`348`/`346` reading the Rust‑built `mlist` + `368`/`349`/`347` terminators), duplicate `+b` (BanExact dedup → re‑`367`, no second node), then `‑b` (→ `del_modeid` + `free_bei`) and the **inverse** re‑query (the ban is gone — mask occurrence count 2→1). New `golden_s2s_ban` (L2‑S2S): a peer **server** sets `:001B MODE #chan +b …` → `set_mode` calls the Rust `add_modeid` with `MyClient(cptr)` **false** (the pure non‑MyClient list‑add arm that skips every `sendto_one`, reachable only over a link), alice's `MODE #chan b` query confirms the ban, then the peer server `‑b` → `del_modeid` + `free_bei` and the re‑query shows it gone. All byte‑identical ref‑C == Rust; full `ircd-golden` suite + the channel L1 diff suite (`channel_leaf/invite/can_send/membership/modes_diff`) green; `cargo build --workspace` 0 warnings; no new canonicalizer masks. **Remaining channel.c (C):** the mode core (`m_mode`/`set_mode`/`send_mode_list`/`send_channel_modes`/`send_channel_members`, sharing the `modebuf`/`parabuf`/`uparabuf` statics) + `reop_channel`/`collect_channel_garbage`/`setup_server_channels`/`free_channel` stay in `channel_link.o`.
- **2026‑06‑03 — P5gg (channel.c mode core — `m_mode`/`set_mode`/`send_mode_list`/`send_channel_modes`/`send_channel_members`) merged.** The MODE command handler (channel.c:1136), the ~860‑line mode parser/applier `set_mode` (:1195), the `+beIR` list emitter `send_mode_list` (:931), and the two server‑burst emitters `send_channel_modes` (:1022) / `send_channel_members` (:1066) ported to `ircd-common/src/channel.rs`, extending the existing `channel_link.o` partial port (`‑DPORT_CHANNEL_P5`). **Fourteenth (final large) channel.c sub‑phase** — the connected component over the shared file‑static scratch buffers `modebuf`/`parabuf`/`uparabuf`. Plan: `docs/superpowers/plans/2026-06-03-p5gg-channel-mode-core.md`.
  - **All five take a plain `#ifndef PORT_CHANNEL_P5` guard — no extern‑prototype switch.** `set_mode`/`send_mode_list` are file‑`static` in C, and after this port have **no remaining C caller** (their only callers — `m_mode` and `send_channel_modes` — port here; `m_njoin`, the only other `modebuf`/`parabuf` user, is already Rust from P5ee) → they become **private Rust `unsafe fn`s** (no `cref_` oracle → L2/L2‑S2S tested). `send_channel_modes`/`send_channel_members` are **exported** (channel_ext.h) and still called by the C `send_server_burst` (s_serv.c:1302‑1311) → `#[no_mangle]` resolves s_serv's calls to the Rust defs at link. `m_mode`'s decl parse.rs imports from `ircd_sys::bindings` resolves once the C def drops. RED confirmed via exactly three undefined symbols (`m_mode`/`send_channel_modes`/`send_channel_members`); the two statics vanished with their sole callers. After the port the C `modebuf`/`parabuf`/`uparabuf` file‑statics have **zero** C users → reuse the existing module‑private `MODEBUF`/`PARABUF` (P5ee in‑call scratch) + new `UPARABUF`.
  - **Callees, all already resolved:** Rust — `canonize` (P5m), `strtoken` (P1), `clean_channelname`/`find_channel`/`is_chan_op` (P5t), `check_channelmask` (P5u), `channel_modes` (P5w), `change_chan_flag` (P5x), `find_chasing` (P5bb), `check_string`/`make_bei`/`free_bei`/`add_modeid`/`del_modeid` (P5ff), `del_invite` (P5u), `find_uid` (P4), `m_umode` (P5h — the non‑channel MODE target route); C extern — the variadic senders (`sendto_one`/`sendto_match_servs_v`/`sendto_channel_butserv`/`sendto_channel_butone`/`sendto_flag`, all already in the block), `myrand` (P2). New imports: `ircstp` (the `is_rreop`/`is_fake` counters), `m_umode`, `SLink__bindgen_ty_1` (the `chops` union init), `ERR_NEEDMOREPARAMS`/`ERR_RESTRICTED`/`ERR_UNKNOWNMODE`, `RPL_CHANNELMODEIS`/`RPL_UNIQOPIS`/the four `RPL_ENDOF{BAN,EXCEPT,INVITE,REOP}LIST`. New `c_int` consts `MODE_DEL`/`MODE_KEY`/`MODE_LIMIT`/`MODE_EXCEPTION`/`MODE_INVITE`/`MODE_REOPLIST`/`KEYLEN`/`MODE_WPARAS` (the imported `MODE_BAN`/… are bindgen `u32` → cast at use; `MODE_UNIQOP`/`MODE_CHANOP`/`MODE_VOICE` == the `CHFL_*` locals). `USE_SERVICES` off (the `check_services_butone` block skipped); `V29PlusOnly` off (the `opcnt >= MAXMODEPARAMS+1` inner guards compile); `JAPANESE` off (no `jp_valid`).
  - **`chops`/`flags[]` + the tri‑state `whatt`.** `set_mode`'s `static Link chops[MAXMODEPARAMS+3]` (in‑call scratch) → a module‑private `static mut CHOPS: [Link; 6]` (const‑init via the `SLink__bindgen_ty_1{cptr: null}` union); `static int flags[]` (the simple‑mode bit↔char table) → a `const SIMPLE_FLAGS: &[(c_int, u8)]` walked in the exact order. `whatt` (C `u_int`, used as both `MODE_ADD`/`MODE_DEL` and the `1`/`-1`/`0` +/- sign tracker across the emission loops) is kept `c_int` — every value (`0x40000000`/`0x20000000`/`0`/`1`/`-1`) compares identically. `mode->mode` is `u_int` → the working copy `new_` is `c_int` (matching C's `int new`), cast on read/write (all bits < 0x40000, no sign issue). The C switch `break`s (break the switch → continue to `curr++`) became a labeled `'sw` block with `break 'sw`; the `'O'`→`'o'`/`'v'` fall‑through is a `proceed`‑bool computed in an `'ob:` labeled block; the `opcnt` reconstruction's `if (tmplen == -1) break;` (breaks the **for** loop) is `break 'recon`, while the per‑item `MODE_KEY` skips are a nested `'emit_key:` block.
  - **`format!` per user request — N/A, documented in‑module.** The whole cluster builds protocol‑byte strings into the `modebuf`/`parabuf`/`uparabuf` scratch (`*mbuf++`, `strcat`, the `+l` `sprintf("%-15d")`) byte‑for‑byte as C (arbitrary client nick/ban‑mask bytes — a UTF‑8 `String` would misframe), then hands them to the C variadic senders; there is no owned ASCII reply to `format!`.
  - **Faithfulness notes.** `set_mode` tail: the sender cascade `s = IsServer(sptr) ? serv->sid : sptr->user ? user->uid : sptr->name`; `sendto_match_servs_v(chptr, cptr, SV_UID, …mbuf, upbuf)` (the UID‑form params in `upbuf`, name‑form in `pbuf`); the fake‑vs‑real branch (`IsServer(cptr) && !IsServer(sptr) && !ischop` → `sendto_flag(SCH_CHAN, "Fake:…")` + `ircstp->is_fake++`, else `sendto_channel_butserv(…pbuf)`); `return ischop ? count : -count`. The `k` key sanitizer (`,`→`.`, the `>0x7f`/`>0xa0` high‑byte mask, KEYLEN truncation) ported on `*mut u8` exactly. `reop` scheduling (`timeofday + LDELAYCHASETIMELIMIT + myrand()%300` for chanop self‑deop / server `+R`; `+r` from server sets `chptr->reop`) writes channel state (not wire — `myrand` non‑determinism is invisible to L2). parseNUH splits `nick!user@host` in place via `strchr`/`strrchr`, `make_bei`, restores the separators.
  - **Bug caught by the burst S2S test (the inverse paid off).** `aListItem.nick`/`user`/`host` are `*mut c_char` **pointers**, not arrays — the first port took `addr_of!((*al).nick)` (address of the pointer field) and `%s` printed the pointer's bytes as garbage (`:000A MODE #chan +b ``…`). The `golden_s2s_mode` relink‑burst diff caught it immediately; fixed to pass `(*al).nick` directly at all three sites (`send_mode_list`, the `beIR` list query, the reconstruction `nuh`). The simpler L2/outbound tests passed *without* this fix (they never serialize a stored `mlist` entry) — only the burst path re‑emits the list, so the S2S coverage was load‑bearing.
  - **L2 + L2‑S2S are the gates (no fresh L1 — set_mode's state mutation is already L1‑covered via the P5x membership + P5ff ban diffs through it).** New `golden_channel_mode_set` (L2): a chanop voices a second member (`+v bob`) — the relay reaches every member via `sendto_channel_butserv`, observed by bob; NAMES shows `+bob`; the **inverse** `-v bob` → NAMES shows plain `bob` (`change_chan_flag` MODE_DEL clears CHFL_VOICE). The 2‑client harness (the per‑host clone limit is 2, the P5cc finding) makes bob both target and observer. New `golden_s2s_mode` (L2‑S2S, 2 scenarios): **outbound** — a local chanop (the peer ops alice on a remote‑NJOIN'd #chan, since a split local client can't create one) sets `+m` then `+l 5`, and the peer receives the UID‑sourced `:000AAAAAA MODE #chan +m` / `+l 5` (`set_mode`'s `sendto_match_servs_v(SV_UID)` tail, `upbuf` form); **link burst** — a populated #chan, then the peer is dropped and **relinks** (the only way to burst a `#` channel: it can't exist before the single harness peer links), so the second burst replays `:000A NJOIN #chan :000AAAAAA` (`send_channel_members`) + `:000A MODE #chan +tnl 10` / `+b *!*@evil.host` (`send_channel_modes` → `channel_modes` + `send_mode_list`). Regression: `golden_channel_modes`/`ban`/`part` (L2) + `golden_s2s_ban`/`membership` (inbound server MODE through the Rust `set_mode`) stay byte‑identical ref‑C == Rust; full `ircd-golden` + `ircd-testkit` (L1) suites green; `cargo build` 0 warnings; no new canonicalizer masks. **Remaining channel.c (C):** only the **lifecycle group** — `reop_channel`/`collect_channel_garbage`/`setup_server_channels`/`free_channel`/`get_channel` — stays in `channel_link.o`. The channel.c mode + handler clusters are now fully Rust.

- **2026‑06‑03 — P5hh (channel.c lifecycle group — `get_channel`/`free_channel`/`reop_channel`/`setup_server_channels`/`collect_channel_garbage` + the `channel` global) merged.** The channel struct's birth/death cluster ported to `ircd-common/src/channel.rs`. **15th and FINAL channel.c sub‑phase** — every symbol channel.c defines now lives in ircd-common, so **`channel.o` is dropped outright** (added to `PORTED` in `ircd-sys/build.rs`; the P5t…P5gg `channel_link.o` second‑compile + its `-DPORT_CHANNEL_P5` link substitution removed entirely; the `cref` oracle keeps the unguarded `channel.o`). Plan: `docs/superpowers/plans/2026-06-03-p5hh-channel-lifecycle.md`.
  - **Symbols.** `get_channel` (channel.c:2226, file‑static, extern‑switched P5aa) + `free_channel` (:2378, file‑static, extern‑switched P5x) → `#[no_mangle] pub unsafe extern "C"` (their `extern`‑block decls in channel.rs removed; the still‑Rust callers `m_part`/`m_join`/`m_njoin`/`remove_user_from_channel`/`setup_server_channels` resolve to them). `reop_channel` (:3911, purely file‑static, `collect_channel_garbage`'s only caller) → private Rust `unsafe fn` (no oracle). `setup_server_channels` (:812) + `collect_channel_garbage` (:4026) are channel_ext.h exports called from `ircd.c:738` (boot) / `ircd.c:1188` (io_loop timer) → `#[no_mangle]`. The `channel` global list head (:52) → `#[no_mangle] pub static mut channel` (replaces the `channel as channel_list` bindgen import; the two read sites in m_list/the GC updated; the still‑C `s_debug` STATS resolves its `extern` decl to it).
  - **Config findings (locked config).** USE_IAUTH on → `setup_server_channels` creates the 14th channel `&AUTH`; CLIENTS_CHANNEL undef → no `&CLIENTS`; JAPANESE off → `get_channel`'s `jp_chname`/`FLAGS_JP` block dead; DEBUGMODE off → `collect`'s `del`/SCH_NOTICE + `reop`'s SCH_NOTICE blocks dead; LOG_SERVER_CHANNELS off → the still‑C `setup_svchans` (send.c) is just the `find_channel` relink loop. `MyFree(chptr)` → plain `free` (the macro's NULL‑out is dead on a by‑value param). The function‑local statics `max_nb`/`split` in `collect_channel_garbage` → module‑private `static mut MAX_NB`/`SPLIT` (persist across calls, faithful). `reop_channel`'s `!CHFL_REOPLIST` arg = the C logical‑not of a nonzero value = `0` (ported literally). istat decrements use `wrapping_sub` (cf. P5u/P5ff).
  - **Classification: utility/callee TU** (none of the six is a `msgtab` handler). `get_channel`/`free_channel`/`reop_channel` are file‑static → the `--prefix-symbols=cref_` rename keeps them **local** (no oracle); `setup_server_channels`/`collect_channel_garbage`/`channel`/`add_to_channel_hash_table` are exported → `cref_*` oracles exist.
  - **L1** (`ircd-testkit/tests/channel_lifecycle_diff.rs`, the two exported entries vs `cref_`; the three statics transitively covered). Both sides drive **separate** process globals (`channel` vs `cref_channel`, separate channel hash tables init'd per‑test via `inithashtables`/`cref_inithashtables` — NULL until boot otherwise, the L1 segfault finding — separate `istat`). (1) `setup_server_channels` create‑cluster: 14 server channels, identical chname/topic/mode.mode/`users==1`‑lone‑chanop walk (`get_channel` CREATE + global‑list prepend + hash insert + `add_user_to_channel` + topic `strcpy` + mode masks + `setup_svchans` relink). (2) **Inverse**: the oracle'd `remove_user_from_channel(mp, &ERRORS)` → `users`→0, history==0 → `free_channel` unlinks `&ERRORS` from both global lists **and** the hash (find now misses), the other 13 survive. (3) `collect_channel_garbage`: hand‑built `&dead` (empty+expired) vs `&live` → identical return `now+CHECKFREQ`, `&dead` freed (gone from list+hash), `&live` kept. **Finding:** `free_channel` re‑checks the *global* `timeofday >= chptr->history` (not collect's `now` param) → the test pins `timeofday`/`cref_timeofday` to `now` so the expired‑free path fires; the `myrand` reop arm needs a live opless `+r` channel (non‑deterministic) → not entered, review‑covered.
  - **No new L2‑S2S.** None of the six has an `IsServer(cptr)` dispatch branch or formats remote‑user fields (`get_channel`'s only client test is `MyClient(cptr)` chname truncation). `get_channel`/`free_channel` are already L2/S2S‑covered transitively by every JOIN/PART golden (`golden_channel*`, `golden_s2s_membership` — remote NJOIN/PART drive `get_channel(CREATE)`/`free_channel`); `collect_channel_garbage`/`reop_channel` are timer‑driven (300 s, unreachable in a short golden). Regression: full `ircd-golden` (83 tests) + `ircd-testkit` L1 suites byte‑identical ref‑C == Rust; `cargo build` 0 warnings; no new canonicalizer masks. **channel.c is now FULLY ported to Rust** — only the channel **lifecycle** remained after P5gg, and this closes it.

- **2026‑06‑03 — P5ii (s_serv.c informational query foundation cluster — `m_version`/`m_time`/`m_admin`) merged.** The three leaf informational query handlers (s_serv.c:109/2534/2547) ported to the new `ircd-common/src/s_serv.rs`. **First `s_serv.c` sub‑phase** — `s_serv.c` is the last (and largest) P5 TU (~3884 lines, ~14 file‑statics, ~40 handlers), ported in clusters; this one establishes the `s_serv_link.o` partial‑port mechanism. Plan: `docs/superpowers/plans/2026-06-03-p5ii-s_serv-informational.md`.
  - **Mechanism — establish `s_serv_link.o` (mirrors `s_user_link.o`).** A second compile of `s_serv.c` with the three ported handlers `#ifdef`'d out (`-DPORT_SERV_P5`), substituted into the link set via the new `"s_serv.o" => "s_serv_link.o"` map entry in `build.rs` `link_objs()`; the cref oracle keeps the unguarded `s_serv.o` in `CREF_OBJS`. The recipe carries the per‑object define `-DIRCDMOTD_PATH="\"$(IRCDMOTD_PATH)\""` (Makefile `s_serv.o` recipe line 346 — needed by the still‑C `m_motd`), driven through `make --eval` so the recursive path var expands identically. All three handlers take a **plain `#ifndef PORT_SERV_P5` guard** — no extern‑prototype switch: none is a file‑`static` and none is anyone's `static` callee. RED→GREEN confirmed: binary links clean (no undefined, no multiple‑definition) and `nm target/debug/ircd | grep ' T \(m_version\|m_time\|m_admin\)$'` shows all three defined in the binary (came from Rust).
  - **Callees, all already resolved.** C externs — `hunt_server` (still C, s_user.c; bindings decl), `find_admin` (still C, s_conf.c; bindings decl), `sendto_one` (variadic P8 trampoline; local `extern "C"` block, fmt `*mut c_char`). Rust — `replies[]` (P3, via `reply(i)`), `date` (P5c s_misc.rs, `date(0)`), `serveropts` (P5b s_debug.rs; the bindgen `[c_char; 0]` incomplete‑array extern → `addr_of!(serveropts) as *mut c_char` resolves to the real 12‑byte Rust static at link). Globals/consts — `me` (`me.name` self‑pointer; `(*me.serv).sid.as_ptr()` for the VERSION SID), `IRC_VERSION` (bindgen `&[u8;7]`), `HUNTED_ISME` (`0u32`). Inlined macros: `ME`=`me.name`, `BadTo`/`BadPtr` (struct_def.h:787), `IsRegistered`=`status >= STAT_SERVER || status == STAT_ME` (struct_def.h:132).
  - **Classification: handler cluster, no L1.** All three are in `msgtab` (`common/parse.c`) at col 1 (`m_version`/`m_time`) or all‑columns (`m_admin`), min‑params 0 → client‑reachable → **L2**. Each has a `hunt_server` forwarding branch (server‑reachable, exactly like P5z `m_list`) → **L2‑S2S**. `m_admin` additionally has the `IsRegistered(cptr)` head‑gate (unregistered clients skip the forward — a local branch, L2‑covered). **No L1** — like `m_list`/`m_topic`/`m_names`, these are pure wire handlers with **no socket‑free data op** (no allocator/list‑mutation/data‑returning lookup), so there is nothing for an L1 differential to drive.
  - **`format!` per user request — N/A, documented in‑module.** None of the three builds an ASCII/numeric reply string of its own — every wire line goes straight to the C variadic `sendto_one(replies[…], …)`; `date()` is the P5c Rust port returning its own static buffer. There is nothing to format.
  - **L2 + L2‑S2S are the gates.** `golden_s_serv_info` (L2): a lone registered client drives `VERSION`→351 RPL_VERSION, `TIME`→391 RPL_TIME (trailing localtime wall‑clock volatile → masked by the existing `canonicalize()` 391 rule), `ADMIN`→256‑259 RPL_ADMINME…RPL_ADMINEMAIL via minimal.conf's `A` line (`find_admin()` non‑null → the admin path, not ERR_NOADMININFO) — reference‑C == Rust byte‑identical. `golden_s2s_s_serv_info` (L2‑S2S): a local client targets `VERSION/TIME/ADMIN peer.test` → `hunt_server` matches the linked peer and forwards each command out the link; the peer receives byte‑identical forwarded lines under ref‑C and Rust (the forwarding short‑circuit the standalone harness can't reach). `cargo build` 0 warnings; full `ircd-golden` + `ircd-testkit` suites green; no new canonicalizer masks.

- **2026‑06‑03 — P5jj (s_serv.c network‑info query cluster — `m_info`/`m_links` + the file‑static `check_link`) merged.** The two remaining leaf network‑info query handlers (s_serv.c:1400/1438) + their shared per‑link load‑health gate `check_link` (s_serv.c:3419) ported into `ircd-common/src/s_serv.rs`, extending the existing `s_serv_link.o` partial port (`‑DPORT_SERV_P5`; three new guard regions). **Second `s_serv.c` sub‑phase.** Plan: `docs/superpowers/plans/2026-06-03-p5jj-s_serv-info-links.md`.
  - **First `s_serv.c` extern‑prototype switch.** `check_link` is file‑`static` in C and still called by the C remnant `m_stats` (×2) / `m_motd` → the **P5u/P5x mechanism**: its forward decl at s_serv.c:36 flips `static`→`extern` under `#ifdef PORT_SERV_P5` (body guarded out) so the C remnant resolves to the Rust `#[no_mangle]` def at link (linkage‑only, faithful). `m_info`/`m_links` take plain `#ifndef` guards (neither is a static / anyone's static callee — their decls parse.rs imports from `ircd_sys::bindings` resolve to Rust). RED→GREEN: binary links clean and `nm target/debug/ircd | grep -E ' T (m_info|m_links|check_link)$'` shows all three defined from Rust (`check_link` is now `T`, was a local `t`).
  - **Callees, all already resolved.** C externs — `hunt_server`, `sendto_one` (variadic P8 trampoline, local block fmt `*mut c_char`). version.c externs (still C) — `infotext` (`[*mut c_char; 0]` incomplete‑array → `addr_of!` + NULL‑walk), `creation`, `generation`. Rust — `replies[]` (P3 `reply()`), `myctime` (P1 support), `collapse`/`match_` (P1), `svrtop` (P2), `ircstp` (P5c). Globals/consts — `me` (`me.name`/`me.firsttime`/`(*me.serv)…`), `bootopt`+`BOOT_PROT`, `timeofday`, `Status_STAT_CLIENT`, `CHREPLLEN`=8192 (config.h:363, baked as a `const c_int` — not a bindgen const, cf. channel.rs). Inlined macros: `ME`/`BadTo`/`BadPtr`, `MyConnect`=`fd>=0`, `IsMasked`=`x && x->serv && x->serv->maskedby != x`, `DBufLength`=`(d)->length`. **Invariant kept:** `acptr->serv->up->name` is `(*(*(*acptr).serv).up).name` (deref the `*mut aClient`, never `.read()` an `aClient` by value).
  - **Classification: handler cluster, no L1.** INFO `{m_nop,m_info,m_info,m_nop,m_unreg}` + LINKS `{m_links×4,m_unreg}` (msgtab col 1, min‑params 0 → client‑reachable → **L2**); each has a `hunt_server` forward (INFO always; LINKS when `parc>2`) → **L2‑S2S**. **No L1** — like P5ii/`m_list`/`m_names`, pure wire handlers with no socket‑free data op. `check_link` *does* mutate `ircstp` counters, but it is file‑`static` → the `‑‑prefix‑symbols=cref_` rename keeps it **local** (no `cref_check_link` oracle), and its non‑zero branches need a remote client over a *loaded* link (SendQ>65536 / link‑age<60s / lastload timing) which a local client never reaches (`MyConnect` → return 0) and which is timing‑non‑deterministic over S2S → the return‑0 path is L2‑covered (every INFO/LINKS), the load branches are **review‑covered** (no deterministic harness path). So no L1 file.
  - **Two new canonicalizer masks (m_info's volatile 371 trailers).** INFO emits, after the deterministic `infotext[]` AUTHORS block, two `371 RPL_INFO` lines that differ between the separately‑built / separately‑booted ref‑C and Rust runs: `:Birth Date: <creation>, compile # <generation>` (version.c build timestamp + per‑build counter) and `:On‑line since <ctime(me.firsttime)>` (boot wall‑clock). Added a 371 rule to `canonicalize()` masking each volatile tail (keeps the label). `infotext[]` itself is byte‑identical across builds → not masked.
  - **`format!` per user request — N/A, documented in‑module.** Every wire line (INFO text/birth/online, the per‑server `364 RPL_LINKS`, the TRYAGAIN/ENDOF replies) goes straight to the C variadic `sendto_one`; `myctime()` returns its own static buffer. `m_links`'s only integer arg is `acptr->hopcount` (`%d`, already `c_int` — the P5c width rule is satisfied). Nothing to format.
  - **L2 + L2‑S2S are the gates.** `golden_s_serv_info_links` (L2): a lone registered client drives `INFO`→a run of `371` (masked birth/online) ending in `374 RPL_ENDOFINFO`, then bare `LINKS`→`364`/`365 RPL_ENDOFLINKS` — ref‑C == Rust byte‑identical; `check_link` returns 0 (local `MyConnect`) so no `263 RPL_TRYAGAIN`. `golden_s2s_s_serv_info_links` (L2‑S2S): the `hunt_server` forwards (`INFO peer.test`, `LINKS peer.test *`) reach the linked peer byte‑identical, **and** the bare‑LINKS tree walk before vs after the peer links (the positive/inverse pair — the lone server sees only `:irc.test 364 alice irc.test irc.test :0 000A L2 Test Server`, then after the link adds `:irc.test 364 alice peer.test irc.test :1 001B Peer Test Server`), the only way to exercise `m_links`'s remote‑server `up->name`/`sid`/`info` field formatting. `cargo build` 0 warnings; full `ircd-golden` + `ircd-testkit` suites green.
