## Progress log

- **2026‑06‑02 — P0a/P0b/P0c merged** (scaffold + Rust‑driven baseline; bindgen full ABI + layout drift‑net; L1 differential harness `ircd-testkit` with the `cref_*` symbol‑renamed oracle archive). See `docs/superpowers/plans/2026-06-01-p0{a,b,c}-*.md`.
- **2026‑06‑02 — P1 (Leaf / pure utils) merged.** New library crate **`ircd-common`** holds the ports (`#[no_mangle] pub extern "C"`, exact `*_ext.h` signatures); `ircd-rs` + `ircd-testkit` depend on it via a `link_anchor()`. Ported & dropped from the C link set: `match.c` (5 fns + 3 tables), `dbuf.c` (8 fns + `poolsize`/`freelist`), and a pure subset of `support.c` (`mystrdup`, `strtoken`, `myctime`, `mybasename`, `inetntop`, `inetpton`, `irc_memcmp`).
  - **Seam wiring (resolves P0c's deferred task):** `ircd-sys/build.rs` now splits `CREF_OBJS` (full set → feeds the `cref_*` oracle) from the link set (`CREF_OBJS \ PORTED`, with `support.o`→`support_link.o` substitution). `support.c` is partially ported via inert `#ifndef PORT_SUPPORT_P1` guards + a second compile, leaving the pristine reference build and the oracle untouched.
  - **Scope corrections vs. original plan:** `irc_sprintf.c` + `snprintf_append` moved to **P8** (variadic `va_arg`, unportable on stable Rust, no post‑format core); `dgets`/`make_isupport`/`ipv6string` left C (not pure leaves). `dbuf_copy` is dead/commented‑out C (not a compiled symbol) → not ported.
  - **Verification:** `cargo build` warning‑free; all L1 + layout tests green; `match`/`dbuf_*`/`mystrdup`/`irc_memcmp` resolve to Rust in `libircd_c.a` while `cref_*` remain in `libcref.a`. Spec: `docs/superpowers/specs/2026-06-02-p1-leaf-utils-design.md`; plan: `docs/superpowers/plans/2026-06-02-p1-leaf-utils.md`.
  - **Faithfulness notes (caught in review):** reproduced `match()`'s always‑active `MAX_ITERATIONS 512` cap; `dbuf_get` mirrors C's `&&` short‑circuit (avoids `dbuf_map`'s `tail` side effect); `dbuf` `poolsize` hard‑codes the compile‑time `(BUFFERPOOL>1500000)?…:1500000` value (correct for this config, pinned by an L1 test); `irc_memcmp` = unsigned byte compare (only the sign is observable); `istat` externed from C, not redefined.
- **2026‑06‑02 — P2 (Data & lookup) merged.** All five modules ported into `ircd-common` (`patricia.rs`, `class.rs`, `whowas.rs`, `hash.rs`, `list.rs`), each with an L1 differential test in `ircd-testkit`. Dropped from the link set: `patricia.o`, `class.o`, `list.o`; substituted: `whowas.o`→`whowas_link.o`, `hash.o`→`hash_link.o`, and `support.o`→`support_link.o` now also `-DPORT_SUPPORT_P2` (so `MyMalloc`/`MyRealloc` come from Rust). Spec: `docs/superpowers/specs/2026-06-02-p2-data-lookup-design.md`; plan: `docs/superpowers/plans/2026-06-02-p2-data-lookup.md`.
  - **Verified config findings that shaped the port:** `ENABLE_CIDR_LIMITS` is **ON** (config.h:738) → `patricia.c` is live, `add_class` carries the trailing `cidrlen_s`, `aClass` has the CIDR fields, `free_class` calls `patricia_destroy`. `USE_HOSTHASH`+`USE_IPHASH` **ON** → `hash.c` has 6 tables. **`MyFree` is a macro** (`sys_def.h:26`), not a symbol → the "trio" is really `MyMalloc`+`MyRealloc`+`outofmemory`; Rust ports call libc `free`.
  - **Partial‑port mechanism (extends P1's `support_link.o`):** `m_whowas` and `m_hash` are command handlers (P5) that format replies through the variadic `sendto_one`/`replies[]` path, so they stay C. The CREF oracle compiles the pristine unguarded `.o`; the link set compiles a `<mod>_link.o` with `-DPORT_<MOD>_P2` that `#ifdef`s out the ported fns + their owning globals and `extern`s the now‑Rust‑owned ones. To make the oracle export the needed `cref_*`, file‑static data (`was`/`locked` in whowas; the tables/counters/`hashtab` in hash) was given external linkage — behaviourally inert.
  - **Determinism preserved (pinned by L1):** bit‑exact hash fns (`hashtab[i]=tolower((char)i)*109`, nick `(<<4)+`, uid `(<<4)^`, channel start‑5/cap‑30/`+(i<<1)`, host/ip `31*+`), head‑prepend insertion, stored‑hashv‑then‑`mycmp` find; whowas ring index advance/wraparound + the `uwas` link storing the **index** not a pointer; `make_client` self/interior pointers + the `CLIENT_REMOTE_SIZE = offset_of!(Client,count)` branch; `make_user`/`make_server` refcnt=1 and `free_user`/`free_server` `-211000/-211001` recursion sentinels.
  - **Also fixed:** a pre‑existing P1 flake — the four `dbuf_diff` tests raced on the shared `poolsize`/`freelist` under parallel `cargo test`; now serialized through a process‑wide mutex.
  - **Verification:** `cargo build` warning‑free; the Rust‑driven `ircd` binary links with `list.o` dropped (all alloc/list logic now Rust); every `*_diff` L1 test zero‑diff vs `cref_*`; layout net green. Deferred to L2/post‑P3: the `m_whowas`/`report_classes` `sendto_*` wire output.
- **2026‑06‑02 — P3 (Transport & format) merged.** Ported into `ircd-common`: `s_err.rs` (the 1000‑entry `replies[]` numeric→format table), `s_id.rs` (UID/SID/CID), `s_numeric.rs` (`do_numeric`), `send.rs` (the non‑variadic delivery core `send_message`/`send_queued`/`flush_connections`/`flush_fdary`). Dropped from the link set: `s_err.o`, `s_id.o`, `s_numeric.o`; substituted: `send.o`→`send_link.o`. Spec: `docs/superpowers/specs/2026-06-02-p3-transport-format-design.md`; plan: `docs/superpowers/plans/2026-06-02-p3-transport-format.md`.
  - **Keystone finding (refines the hazard table):** the entire `sendto_*` (send.c) + `esendto_*` (s_send.c) family is **irreducibly C trampolines** — every one is C‑variadic and consumes `va_list` through a formatter (`vsendprep`/`vsendpreprep`/`build_suffix`), and `vsendpreprep` (send.c:384) consumes `va_arg` **per recipient**, so there is no "format once → Rust core" reduction. The Rust "cores" P3 actually extracts are the **delivery** fns, not the senders. The senders + `vsendto_one`/`sendto_flog`/`logfiles_*`/`setup_svchans` + the static buffers/`anon`/`svchans` stay C; they vanish in **P8** when call sites adopt `format!`. **s_send.c is therefore 100% C in P3 (nothing ported).**
  - **Verified config gates (all OFF):** `USE_SERVICES`, `LOG_SERVER_CHANNELS`, `USE_SYSLOG`, `LOG_OLDFORMAT`, `ZIP_LINKS`, `CLIENTS_CHANNEL`, `JAPANESE`, `POOLSIZE_LIMITED` → the corresponding branches of `send_message`/`send_queued`/`sendto_flog`/`setup_svchans` are omitted from the Rust port. `SCH_MAX`=14.
  - **`send_link.o` partial‑port (extends P1/P2):** `send.c` compiled twice — pristine `send.o` for the oracle; `send_link.o` with `-DPORT_SEND_P3` that `#ifndef`‑guards the 4 ported delivery fns out of the link set **and exposes `dead_link`** (drops `static`) so the Rust core can call that still‑variadic C fn. Built via `make --eval` so the recursive `-DFNAME_USERLOG/CONNLOG/SCH_PREFIX` path vars expand identically for the surviving C `logfiles_open`. Pristine `send.o` (`dead_link` static, `send_message` defined) is unchanged.
  - **Faithfulness notes (caught in review):** `replies[]` is **not** mechanically text‑extractable — it contains `#if/#else/#endif` config branches (WHOIS_SIGNON_TIME/USERS_RFC1459/ENABLE_SUMMON/TXT_NOSTATSK), two bare‑`NULL` entries, and an escaped `\r\n` (entry 384) that Rust source‑level CRLF normalization would silently corrupt → it is **dumped from the compiled `cref_replies` oracle** with byte‑level escaping. `alphabet_id[256]` in s_id.c is partially initialized (249 explicit values) so indices 249..=255 default to **0** (reproduced verbatim). `init_sid` is `#if 0` (not ported). `poolsize *= 1.1` reproduced as `(poolsize as f64 * 1.1) as u_int`.
  - **L1 coverage / L2 deferrals:** L1 zero‑diff vs `cref_` on the `replies[]` table (all 1000 slots), `ltoid`/`idtol` round‑trip + `sid_valid`/`cid_ok`/`check_uid`, `do_numeric`'s early‑out guards, and `send_message` sendQ buffering (bytes + `sendM` + `lastsq`) + `send_queued`'s dead/empty early returns. **Deferred to L2** (need full server state — initialized hash tables, registered `me`, `cref_`'s separate globals, a live fd): the `do_numeric` broadcast wire output and the `send_queued` `deliver_it` write path.
  - **Verification:** `cargo build` warning‑free; Rust‑driven `ircd` links (`send_link.o`'s variadic senders call the Rust `send_message`; the Rust core calls C `dead_link`/`deliver_it`); all `*_diff` L1 tests zero‑diff; layout net green.
- **2026‑06‑02 — P4 (Parser & dispatch) merged.** All of `common/parse.c` ported into `ircd-common/src/parse.rs` and **`parse.o` dropped outright** (added to `PORTED`, like `list.o`/`match.o`) — no `parse_link.o`, because **every** symbol parse.c defined is portable now: the `msgtab[]` table, all 11 `find_*` wrappers, `parse`, `getfield`, the four stub handlers (`m_nop`/`m_nopriv`/`m_unreg`/`m_reg`), and the three file‑statics (`find_sender`/`cancel_clients`/`remove_unknown`). Spec: `docs/superpowers/specs/2026-06-02-p4-parse-dispatch-design.md`; plan: `docs/superpowers/plans/2026-06-02-p4-parse-dispatch.md`.
  - **Refines the hazard table (variadic dispatch):** the ~14 `sendto_*` senders parse.c **calls** stay C, but Rust **calls** them through bindgen variadic declarations (`sendto_one(to, c"…".as_ptr(), …)`) — the exact s_numeric.rs pattern. *Calling* a C variadic is stable Rust; only *defining* one is not. So no trampoline is needed on the call side; the senders themselves still vanish in P8.
  - **`msgtab[]` as a Rust `#[no_mangle] static mut [Message; 66]`:** built with a `row!`/`m!` macro; handler fn‑pointers reference the still‑C `m_*` (and the four Rust stubs) and coerce in the `static` initializer; the mutable `count`/`rcount`/`bytes`/`rbytes` counters mutate in place exactly as C (read by STATS in still‑C `s_serv.o`). **Config‑resolved** for this build: `TKLINE` **on** (TKLINE+UNTKLINE rows), `USE_SERVICES`/`KLINE`/`ENABLE_SIDTRACE` **off** (no SERVSET/KLINE/SIDTRACE rows), `MOTD_UNREG` **off** (MOTD unreg slot = `m_unreg`), `IDLE_FROM_MSG` **on** (idle guard = `fhandler == m_private`), `DEBUGMODE` **off** (every `Debug(())` omitted). The msgtab L1 test compares cmd/min/max/row‑count, **not** handler pointers (Rust rows point at `m_join`, the oracle at `cref_m_join`).
  - **Bug caught by L1:** `IRCDCONF_DELIMITER` is `'|'` (config.h:500), **not** `':'` — the first `getfield` draft used `':'` and the differential failed immediately. (find_mask/find_name L1 also surfaced that the hash tables are null until `inithashtables()`; the test now inits both sets.)
  - **L1 coverage / L2‑L3 deferrals:** L1 zero‑diff vs `cref_` on the `msgtab[]` structure, `getfield` (delimiter/escape/trailing/empty), `find_mask`/`find_name` empty‑hash fallbacks, and `parse()`'s no‑`me`/no‑hash/no‑sender paths (IsDead early‑out, empty message, unknown‑command‑from‑non‑person, 3‑digit numeric, and an `ENCAP→m_nop` dispatch — asserting return code + `cptr.flags` + `ircstp` counter deltas). **Deferred to L3 (parser fuzz) / L2:** the full `parse()` post‑state differential (dispatch into the still‑C `m_*`, mutated `aClient` + handler counters) and hash‑populated `find_*` — both need the populated‑server fixture.
  - **Deliverable:** the per‑handler‑file static‑symbol **cluster map** for P5 (`docs/superpowers/specs/2026-06-02-p5-handler-cluster-map.md`): clusters + entry points + suggested porting order (s_zip/s_debug → s_auth/s_service → s_user → s_misc → channel.c → s_serv.c).
  - **Verification:** `cargo build` warning‑free; the Rust‑driven `ircd` binary links with `parse.o` dropped (`parse`/`msgtab`/`find_*`/`getfield`/`m_nop` resolve to Rust); full `cargo test` green (all prior `*_diff` + `parse_diff` 4/4 + layout); 0 warnings.
- **2026‑06‑02 — P0‑L2 (golden harness) built.** New workspace crate **`ircd-golden`** boots a pristine **reference‑C `ircd`** and the in‑progress **Rust‑driven `ircd`** side‑by‑side, drives a scripted TCP client one action at a time, canonicalizes, and asserts byte‑identical wire output. First scenario — client registration (NICK/USER → welcome banner) — is **byte‑identical** between the two builds (`alice!~alice@127.0.0.1`, 16 lines through `422 MOTD File is missing`). This is the real **P5** gate (command handlers can't be L1‑tested). Spec/plan: `docs/superpowers/plans/2026-06-02-p0-l2-golden-harness.md`.
  - **Reference‑C runnable binary (new):** the L1 oracle only ever produced *renamed objects*, never a runnable C ircd. `ircd-golden/build.rs` now builds one: recompile **only `ircd.c`** with a native `main` (`cbuild/ircd.o` carries `‑Dmain=c_ircd_main`, so it has none) into `ircd_ref.o` via `make ‑‑eval` (so the per‑object path defines `$(IRCDCONF_PATH)`/… expand identically), then link it with the **pristine base objects** (the plain `.o`, not the `_link.o` partial‑port variants) + `version.o` + `‑lz ‑lm ‑lcrypt`. Built into `OUT_DIR`; path exported via `cargo:rustc‑env`.
  - **DNS disabled project‑wide (`NO_DNS_LOOKUP`):** `s_bsd.c add_connection`'s async reverse‑PTR lookup (`gethost_byaddr`→`SetDNS`) both embedded a nondeterministic resolved hostname in the banner **and** held registration across event‑loop iterations. Now `‑DNO_DNS_LOOKUP` (added to `EXTRA_CFLAGS` in `ircd‑sys/build.rs`, so cref oracle + link set + Rust binary all share it) makes the lookup a no‑op: `hostp` stays NULL without `SetDNS`, clients register immediately with their **numeric** host (the same end‑state a no‑PTR lookup reaches; handled at the existing `!cptr->hostp` paths). The `USE_IAUTH` cache‑hit block that derefs `hostp->h_aliases` is compiled out under the same flag. Per the user: resolver behaviour is intentionally **not** maintained.
  - **Keystone finding — DO NOT pin the clock to a constant.** The PLAN's "LD_PRELOAD shim overriding `time()`/`gettimeofday()` to a fixed value" **breaks the server**: a frozen `time()` freezes the global `timeofday` (set every loop at `ircd.c:1250`), so the io_loop's scheduling never advances and the **listen socket is never serviced** — clients are never accepted (confirmed via `strace`: `poll()` only ever watched the resolver fd). The harness uses the **real clock** and relies on the **canonicalizer** to mask the few time‑bearing tokens (`242` uptime, `317` idle/signon, `391` time; `003`/`004` are compile‑time‑stable across both builds). The registration banner embeds no runtime timestamps, so it needs none. (A future advancing‑from‑fixed‑epoch shim could tighten WHOIS‑idle/STATS scenarios.)
  - **Config path redirect (no root):** `CMDLINE_CONFIG` is `#undef` → `‑f` is dead; both binaries `open("/usr/local/etc/ircd.conf")` (`s_conf.c:1395`). A committed LD_PRELOAD shim (`ircd-golden/csrc/l2shim.c`) intercepts `open`/`open64` and rewrites that one path to `$IRCD_TEST_CONFIG`. Boot flags: `‑t` (foreground) `‑s` (`BOOT_NOIAUTH`). Both bind the same fixture port (16667), so the test runs them **sequentially**, never concurrently.
  - **Verification:** `cargo test ‑p ircd‑golden` → `registration_banner_matches_reference` PASS; full `cargo test` green (golden + all 12 `*_diff` L1 suites + layout 4/4); `s_bsd.c` is not L1‑tested so the DNS edit breaks no differential; 0 warnings.
- **2026‑06‑02 — P5a (s_service.c — first command‑handler cluster) merged.** All 6 compiled symbols (`svctop`, `make_service`, `free_service`, `best_service` [static], `m_service`, `m_servlist`, `m_squery`) ported to `ircd-common/src/s_service.rs` and **`s_service.o` dropped outright** (added to `PORTED`, like `parse.o`). Establishes the **P5 handler‑porting mechanism**: a Rust `m_*` reachable through the `msgtab` fn‑pointer seam. Spec/plan: `docs/superpowers/plans/2026-06-02-p5a-s_service.md`.
  - **parse.rs needs no change:** it already imports `m_service`/`m_servlist`/`m_squery`/`svctop` from `ircd_sys::bindings` (it imports *all* symbols there, even already‑ported ones); those declarations resolve to the Rust definitions at link once `s_service.o` is dropped. This is the general seam for every future handler — define the Rust symbol, drop the `.o`, no msgtab edit.
  - **Variadic senders:** Rust **calls** `sendto_one`/`sendto_flag` via a local `extern "C"` block (the s_numeric.rs pattern); fmt must be declared `*mut c_char` (matching the bindgen/other‑module decls) to avoid the `clashing_extern_declarations` lint, so format‑string literals are cast `c"…".as_ptr() as *mut c_char`. `MyFree` = libc `free`; `strncpyzt`/`index`(=`strchr`)/`myncmp` ported.
  - **Config (USE_SERVICES off) shapes the port:** `m_servset`/`check_services_*`/`find_conf_service` + the local‑service branch of `m_service` are not compiled and not ported; a client `SERVICE` reaches the `#ifndef USE_SERVICES` reject (`ERROR :Server doesn't support services`).
  - **msgtab dispatch findings (caught at L2):** a *registered* client's `SERVICE` maps to `m_nop` (msgtab index 1 = registered‑client column is `m_nop`), so it produces no reply — `m_service` is only reached from index 0 (unregistered) / 4 (server→SERVICE). Bare `SQUERY` is rejected by `parse()` with `461` (min‑params) before the handler. So L2 coverage of the ported code is: `m_servlist` (`235`), `m_squery`+`best_service` (`408`, empty list), and `m_service` reject (unregistered client → `ERROR`).
  - **L2 harness hardening:** `ircd-golden` boots now serialize through a process‑wide `PORT_LOCK` held by the `Ircd` guard — `cargo test` runs `#[test]`s on parallel threads and they all bind the one fixture port (16667). (Single‑test `golden_registration` had masked this; the 2‑test `golden_service` surfaced it.)
  - **Verification:** L1 `s_service_diff` zero‑diff vs `cref_` (`make_service` svctop linkage + `cptr->name` self‑pointer + idempotency; `free_service` head/middle unlink + `service=NULL`); L2 `golden_service` 2/2 byte‑identical (registered SERVLIST/SQUERY + unregistered SERVICE reject); the Rust `ircd` links with `s_service.o` dropped (`m_service`/`svctop` resolve to ircd‑common); full `cargo test` green; 0 warnings.
- **2026‑06‑02 — P5b (s_debug.c — first utility/callee TU) merged; s_zip.c dropped.** All 4 compiled symbols (`serveropts` config‑resolved option string, `send_usage`, `send_defines`, `count_memory`) ported to `ircd-common/src/s_debug.rs` and **`s_debug.o` dropped outright**; **`s_zip.o` also added to `PORTED`** (ZIP_LINKS off → its only symbol is a `static rcsid`, nothing to port). The variadic `debug()` is `#ifdef DEBUGMODE` (off) → not compiled, not ported. This is the first **utility TU** (no `msgtab` entry): the three report fns are reached from still‑C `m_stats` (s_serv.c) via STATS letters.
  - **Classification → L2 strategy:** s_debug has no client‑reachable `m_*`; coverage comes from driving the *caller's* command. STATS `d`→`send_defines` (RPL_STATSDEFINE 248, all compile‑time constants — fully deterministic), STATS `z`→`count_memory(.., 0)` (RPL_STATSDEBUG 249 memory counts from `istat`/sizes). Both reachable by a plain registered local client (no oper gate on `d`/`z`). STATS `r`/`R`→`send_usage` is **not** L2‑covered (getrusage CPU/RSS fields are wall‑clock volatile); ported faithfully (HAVE_GETRUSAGE path, `hzz=1`) + links, but exercised only by code review.
  - **Config‑resolved data, flattened (not `#ifdef`‑replicated):** `serveropts` = `"aEFJKMQRTu6"` (HUB off, CLONE_CHECK on, OPER_* on, IDLE_FROM_MSG on; dumped from the oracle `.o` and pinned by L1). `send_defines` bakes ~30 macro constants bindgen drops (MAXSERVERS=1 [HUB off], CLONE_MAX/PERIOD=10/2, ZL=-1, DELAY_CLOSE=15, MINLOCALNICKLEN→1, …) as resolved literals — the STATS d L2 byte‑diff is the safety net for any wrong constant. `BUFFERPOOL` resolved to 222320 (= DBUFSIZ 2032·MAXCONN 50·2 + QUEUELEN 19120·MAXSERVERS 1).
  - **Determinism / canonicalizer:** `count_memory`'s final `:TOTAL: <tot> sbrk(0)-etext: <N>` line carries a volatile heap‑break token (differs between the separately‑built ref‑C and Rust binaries); added one canonicalizer mask (truncate after `sbrk(0)-etext:`). Everything else in STATS z is deterministic — `cres_mem` sends two RPL_STATSDEBUG lines but with the DNS cache empty (NO_DNS) they're all‑zero, and `tot` is config/counter‑derived. `count_memory`'s `if(debug)` [REAL] branches (the client/channel/conf list walks) are ported faithfully but never execute on the STATS z path (`debug=0`).
  - **`libc` for `getrusage`/`rusage`:** sys/resource.h isn't in bindgen's wrapper.h, so `send_usage` uses `libc::{rusage, getrusage, RUSAGE_SELF}` rather than a hand‑rolled struct; `sbrk`/`time`/`strerror` via a local `extern "C"` block. Variadic `sendto_one` called the s_service.rs way (fmt `*mut c_char`, literals cast).
  - **Verification:** L1 `s_debug_diff` zero‑diff vs `cref_serveropts` (+ pins the resolved value); L2 `golden_debug` 2/2 byte‑identical (STATS d `send_defines` block incl. `:HUB:no MS:1`; STATS z `count_memory` block incl. masked `sbrk(0)-etext: <BRK>`); the Rust `ircd` links with `s_debug.o`+`s_zip.o` dropped (`serveropts`/`send_*`/`count_memory` resolve to ircd‑common); full `cargo test` green; 0 warnings.
- **2026‑06‑02 — P5c (s_misc.c — the exit cascade + misc utilities) merged.** All 18 ext‑header symbols + the two file‑statics (`exit_server`/`exit_one_client`) + the 4 exported globals (`ircst`/`ircstp`, `motd`/`motd_mtime`) ported to `ircd-common/src/s_misc.rs`; **`s_misc.o` dropped outright** (every symbol portable → added to `PORTED`, no `_link.o`). This is the largest utility TU so far (1217 LOC): `exit_client`→`exit_server`↔`exit_one_client` is the mutually/self‑recursive client/server exit cascade (the critical network path), plus `date`/`get_client_{name,host,ip}`/`get_sockhost`/`my_name_for_link`/`mark_blind_servers`/`tstats`/`read_motd`/`check_split`/`check_registered*`/`checklist`/`initstats`/`initruntimeconf`/`myrand`/`mysrand`. Spec/plan via the command‑cluster‑port skill.
  - **Config gates that shaped the port (all verified in cbuild/config.h):** `USE_SERVICES`/`UNIXPORT`/`USE_SYSLOG`/`FNAME_USERLOG`/`FNAME_CONNLOG`/`CLIENTS_CHANNEL`/`BETTER_CDELAY`/`BETTER_NDELAY` **off**; `DELAY_CLOSE=15`/`USE_HOSTHASH`/`USE_IPHASH`/`USE_IAUTH`/`CACCEPT_DEFAULT=2` **on**. Crucially the entire `exit_client` per‑client **logging block** (s_misc.c:465‑507) is gated on `(FNAME_USERLOG||FNAME_CONNLOG||USE_SERVICES||(USE_SYSLOG&&…))` — all undefined → omitted, so **`sendto_flog` is never called from s_misc** (one fewer variadic to thread). The `#ifdef USE_SERVICES` `check_services_butone` calls throughout the cascade are likewise not compiled/not ported.
  - **Keystone finding — variadic `%lu`‑from‑`u_int` is a *caller‑side* faithfulness hazard, caught by L2.** `tstats` passes ~30 `u_int` (32‑bit) `istat`/`stats` fields straight to `%lu` (64‑bit) — UB that prints correctly in C only because the SysV ABI zero‑extends 32‑bit args into their 64‑bit slots. A Rust variadic call leaves the upper 32 bits **undefined** → `STATS t` printed garbage (`refused 94888712470528`). Fix: cast every integer arg to *exactly* its conversion width (`%lu`→`u_long`, `%llu`→`c_ulonglong`, `%d`→`c_int`, `%u`→`c_uint`); the decimal value is unchanged (all fields < 2³²) so the wire output is byte‑identical to a correct C run. **This is now a standing rule for any ported fn that calls the C variadic senders with sub‑`long` integers.** (The `golden_s_misc` STATS t diff is what surfaced it — exactly what L2 is for.)
  - **`format!`/byte‑builder instead of libc `sprintf` (per user request):** the buffer‑formatting fns no longer call `sprintf`. `date` (pure ASCII) uses Rust `format!` then copies into its static `[c_char;80]`. `get_client_{name,host}` + `exit_client`'s `comment1` use small byte‑exact `push_cstr`/`push_cstr_prec`/`store` helpers — **not** `format!`, because C's `%.*s` byte‑precision over arbitrary (possibly non‑UTF‑8) C‑string bytes can't pass through a UTF‑8 `String` without corruption. The senders (`sendto_one`/`sendto_flag`/`sendto_serv_butone`/`sendto_common_channels`/`sendto_channel_butserv`) stay C variadics (P8). (Caught en route: `&str::as_ptr()` is **not** NUL‑terminated → an early `sprintf %s` overran into adjacent rodata; the byte‑builder rewrite removes that footgun class.)
  - **Globals owned here:** `ircst`/`ircstp` (`*ircstp = &ircst`) as `#[no_mangle] static mut` — `ircst` zero‑inits via `MaybeUninit::zeroed().assume_init()` (const), `ircstp` via `addr_of_mut!(ircst)`; `motd`=NULL, `motd_mtime`=0. `months`/`weekdays` stay module‑private `&str` tables. All Is*/Got*/strncpyzt macros reproduced inline; `match`/`mycmp` imported from bindings; hash/list/whowas callees (`del_from_*_hash_table`/`remove_client_from_list`/`unregister_server`/`add_history`/`off_history`) resolve to the Rust ports; channel/conf/bsd callees (`remove_user_from_channel`/`del_invite`/`find_conf_name`/`close_connection`/`activate_delayed_listeners`/`is_chan_op`/`remove_server_from_tree`/`report_iauth_stats`/`dgets`) stay C.
  - **Verification:** L1 `s_misc_diff` zero‑diff vs `cref_` (date over 4 epochs; my_name_for_link dot‑walk/guard; get_sockhost `@`‑strip; get_client_ip `::ffff:` inetntop; initstats/initruntimeconf seeded fields; mysrand/myrand determinism). L2 `golden_s_misc` 2/2 byte‑identical: **QUIT** → `exit_client` local path (`ERROR :Closing Link: alice[~alice@127.0.0.1] ("gone fishing")`), **STATS t** → `tstats` (full RPL_STATSDEBUG block; `:time connected` masked as wall‑clock volatile). Added two canonicalizer masks: `:time connected` and a drop of the racy `SPLIT_CONNECT_NOTICE` (register‑time NOTICE whose post‑422 TCP chunking is nondeterministic). Full `cargo test` green; 0 warnings. **Deferred to S2S/multi‑client L2:** the `exit_one_client` channel‑QUIT broadcast, server `SQUIT` propagation, masked‑server paths (ported faithfully, covered by review).
- **2026‑06‑02 — P5d (s_user.c WHO/WHOIS cluster) merged.** The contiguous WHO/WHOIS display cluster (s_user.c:1685‑2315 — `who_one`/`who_channel`/`who_find` [statics], `parse_who_arg` [file‑global], `send_whois` [static], `m_who`/`m_whois` [exported, msgtab]) ported to `ircd-common/src/s_user.rs`. **First in‑TU partial port of P5:** `s_user.o`→`s_user_link.o` (`‑DPORT_USER_P5` `#ifndef`‑guards the cluster out of the link set; the cref oracle keeps the full unguarded `s_user.o`). The rest of s_user.c (register_user/m_nick/m_kill/m_umode/m_oper/m_message/… + the shared statics `buf`/`buf2`/`user_modes`) stays C. `s_user_link.o` built via `make --eval` so the per‑object `FNAME_USERLOG/CONNLOG/OPERLOG` path defines (used by the surviving C register_user/m_oper) expand identically (the send_link.o pattern).
  - **`s_auth.c` deferred (not a P5 cluster):** it has **no `m_*` handlers** — it's the ident/iauth glue (`start_auth`/`send_authports`/`read_authports`/`read_iauth`/`sendto_iauth`), pure socket/pipe I/O touching `adfd`/`highest_fd`/`local[]`/`start_iauth`, and the golden harness boots `‑s` (BOOT_NOIAUTH → `adfd<0`) so almost none of it runs ⇒ **no L2 path**. Belongs to P7 (I/O core) + P‑IAUTH.
  - **Seam (no parse.rs edit):** parse.rs already imports `m_who`/`m_whois` from `ircd_sys::bindings`; once `s_user.o`→`s_user_link.o` drops them, those decls resolve to the Rust defs. `parse_who_arg` is `#[no_mangle]` only so the L1 test can reach it (no header/external C caller). Still‑C callees (`canonize`/`hunt_server`/`next_client`/`send_away`/`is_chan_op`/`has_voice`) + Rust‑ported callees (`find_client`/`find_channel`/`hash_find_client`/`match_`/`strtoken`/`get_client_ip`/`collapse`/`clean_channelname`/`cid_ok`) reached via bindgen decls; `sendto_one` is the C variadic trampoline.
  - **`format!` per user request:** WHOX (354) is built field‑by‑field into a `Vec<u8>` — numeric fields (`%d` hopcount, `%ld` idle) via `format!`, arbitrary C‑string fields (username/host/info/…) copied raw via `push_cstr` (a UTF‑8 String would corrupt non‑ASCII bytes), then truncated to `BUFSIZE‑1` (faithful to C's incremental `snprintf`/`snprintf_append` truncation). `send_whois`'s channel‑list accumulation keeps the byte‑exact pointer/`len` logic on a private `static mut BUF` (the C file‑static `buf` stays with the C remnant). The placeholder `UnknownUser` anUser (unreachable on the IsPerson path) reproduced as a `static mut` via a `const carr()`.
  - **Config:** WHOIS_SIGNON_TIME **on** → 317 carries the extra signon `%ld` (idle+signon masked by the existing canonicalizer). IsMember active def = `find_channel_link(u->user->channel, c)`. IsChannelName uses Rust `cid_ok`.
  - **Verification:** L1 `s_user_who_diff` zero‑diff vs `cref_parse_who_arg` over 12 WHOX arg strings (`o`/`%`flags/`,token` len‑cap/unknown chars). L2 `golden_s_user` 2/2 byte‑identical: **WHOIS alice** (311/312/317‑masked/318) + **WHO alice** (352/315). The Rust `ircd` links with `s_user_link.o` substituted (`m_who`/`m_whois`/`parse_who_arg` resolve to ircd‑common). Full `cargo test` green; 0 warnings. **Deferred to S2S/multi‑client L2:** WHOX, channel WHO/WHOIS, wildcard WHOIS, the `hunt_server` remote path (ported faithfully, covered by review + the L1 differential).
- **2026‑06‑02 — P5e (s_user.c USERHOST/ISON cluster) merged.** `m_userhost` (s_user.c:3073) and `m_ison` (s_user.c:3127) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port — two more `#ifndef PORT_USER_P5` regions guard them out of the link set; the cref oracle keeps the full unguarded `s_user.o`. **Pure handler cluster** (both are `msgtab` rows, client‑reachable): no parse.rs edit (the `m_userhost`/`m_ison` decls parse.rs imports from `ircd_sys::bindings` resolve to the Rust defs at link). The shared C file‑statics `buf`/`buf2` are replaced by private Rust `Vec<u8>` accumulators — faithful since each is used transiently within a single call (no cross‑call state); the C remnant (`m_umode`/`register_user`) keeps its own `buf`.
  - **`format!` per user request:** both replies are byte‑exact reconstructions of `replies[RPL_USERHOST]`/`replies[RPL_ISON]` (`":%s 30x %s :"`) built field‑by‑field. Every appended field (nick/username/host) is an **arbitrary C‑string copied raw via `push_cstr`** — a UTF‑8 `String`/`format!` would corrupt non‑ASCII bytes — and these handlers have **no integer conversions**, so the only `format!`‑eligible tokens are the literal numerics/separators (kept as byte literals, matching the existing `who_one` 354 reconstruction). Documented in‑module: `format!` is used for ASCII/numeric, raw copy for C‑string bytes.
  - **Faithfulness notes:** `m_userhost` walks up to 5 targets across `parv[]` via the `idx` index (`parv[++idx]`), space‑separates entries (the `if(*buf2)` "not the first entry" guard → tracked by `buf2` staying non‑empty), reproduces the `strncat(buf, buf2, sizeof(buf)-len)` cap **and** C's `len += strlen(buf2)` (the full length even when truncated), and the re‑head/flush at `len > BUFSIZE-(NICKLEN+5+HOSTLEN+USERLEN)=419` (effectively dead for ≤5 short targets, ported anyway). `m_ison` strtoken‑splits the **single** trailing param, appends `name` + a trailing space per hit, and breaks at `len+i > BUFSIZE-4` ("leave room for ` \r\n\0`").
  - **Harness fix (cross‑process boot lock).** Adding a 6th `ircd-golden` test *binary* surfaced a latent flake: `cargo test` runs each test file as its own process and runs several in **parallel**, but the boot serializer was an in‑process `Mutex` — so two binaries (e.g. `golden_debug` + `golden_s_user_userhost`) raced to bind the one fixture port 16667 → `BrokenPipe`/diff failures. Replaced the `Mutex` with a **cross‑process advisory file lock** (`libc::flock(LOCK_EX)` on `fixtures/.port.lock`, held in the `Ircd` guard, released when its fd closes after `child.kill()+wait()`); separate fds contend even within one process, so it also serializes the parallel `#[test]` threads inside a binary. `libc` added as an `ircd-golden` dep; the lock artifact is gitignored.
  - **Verification:** L2 `golden_s_user_userhost` 2/2 byte‑identical (**USERHOST alice nemo** → 302 with the multi‑target `idx` walk + a `find_person` miss on nemo; **ISON :alice nemo** → 303 with a hit + a miss); both replies fully deterministic (no time tokens → no canonicalizer change). The Rust `ircd` links with `m_userhost`/`m_ison` defined `T` from ircd‑common. Full `cargo test` green and **stable across repeated runs** (flake fixed); 0 warnings. **Deferred to multi‑client L2:** the `>BUFSIZE` batch‑flush paths (need many recipients; ported faithfully, covered by review).
- **2026‑06‑02 — P5f (s_user.c message‑routing cluster) merged.** `m_message` (the shared PRIVMSG/NOTICE delivery core, s_user.c:1457) + its two `#[no_mangle]` wrappers `m_private`/`m_notice` ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (the `PORT_USER_P5` guard now spans m_message **through** the WHO/WHOIS cluster as one contiguous region — the redundant inner `#ifndef` was removed). **Pure handler cluster** (both wrappers are `msgtab` rows: PRIVMSG→`m_private` col 1, NOTICE→`m_notice`): no parse.rs edit (the decls parse.rs imports from `ircd_sys::bindings` resolve to the Rust defs once `s_user.o`→`s_user_link.o` drops them). RED confirmed via undefined `m_private`/`m_notice` (`m_message` is `static` → inlined into both, never a link symbol).
  - **First multi‑client L2.** The user required `#`/`+`/`&` channel relay coverage, which needs ≥2 simultaneous clients (`sendto_channel_butone` delivers to the *other* members, never the sender). Added to `ircd-golden`: a persistent **`Client`** struct (`connect`/`register`/`send`/`read_until`/`drain`) and **`boot_standalone`** (`-p standalone`). New `golden_s_user_message` (7 scenarios): PRIVMSG/NOTICE self‑echo, PRIVMSG no‑such‑nick (401), and alice→bob relay on `#chan`/`+chan`/`&chan` (PRIVMSG) + `#chan` (NOTICE). All byte‑identical vs reference‑C.
  - **Keystone finding — a lone server is *permanently* split‑mode, which blocks `#`‑channel creation.** The fixture M‑line's split thresholds are **floored to SPLIT_SERVERS(10)/SPLIT_USERS(5000)** (s_conf.c:1950), so `check_split()` never leaves split (needs 10 eob‑servers + 5000 users). During split, channel.c:2537 (`IsSplit() && UseModes(name) && !(&||!)`) rejects creating global `#` channels with ERR_UNAVAILRESOURCE — but allows `+` (not `UseModes`) and `&` (excluded). So `#` golden tests must boot **`-p standalone`** (`iconf.split = -1` → `IsSplit()` false), which lets all three prefixes be created uniformly. (This is exactly why `+`/`&` passed but `#` failed on the first run.)
  - **Harness gotcha — sentinel collision with the split notice.** The `SPLIT_CONNECT_NOTICE` (`NOTICE alice :…split-mode.`) can arrive *after* the 422 the registration drain stops on (TCP‑chunking race) and contains the substring ` NOTICE alice `, which falsely satisfied the NOTICE‑self `read_until(" NOTICE alice ")`. Fixed by draining after register before sending. (PRIVMSG's sentinel ` PRIVMSG alice ` doesn't collide, so only NOTICE‑self failed at first.)
  - **`format!` per user request — N/A here, documented.** `m_message` is pure routing: every reply is handed to a **C variadic sender** (`sendto_one`/`sendto_prefix_one`/`sendto_channel_butone`/`sendto_match_butone`) as format‑string + raw C‑string args; there is no intermediate string to build, and pre‑formatting would be wrong (a sender rewrites the per‑recipient prefix itself). Senders imported from `ircd_sys::bindings` (bindgen declares `pattern: *mut c_char` → no `clashing_extern_declarations`); `strrchr` (rindex) added to the local extern block.
  - **Faithfulness notes:** `syntax` is declared **outside** the per‑target loop (an oper's `$$`/`$#` match leaks into later targets — verbatim C); the C for‑loop post‑expr `nick = strtoken(…), penalty++` (which must run even on `continue`) is modeled with a labeled `'target` block where each C `continue` → `break 'target`, followed by the advance; the nick!user@host and user[%host]@server paths NUL‑terminate `nick` in place then restore `!`/`@`/`%`, exactly as C mutates the strtoken slice.
  - **Verification:** `golden_s_user_message` 7/7 byte‑identical; `m_private`/`m_notice` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (all 12 `*_diff` L1 + layout + all 7 golden suites); 0 warnings. **Deferred to S2S L2:** the server‑sourced/uid (`find_uid`), `@server`‑forwarding, and oper `$$`/`$#`‑mask broadcast paths (need a server link; ported faithfully, covered by review).
- **2026‑06‑02 — P5g (s_user.c AWAY cluster) merged.** `m_away` (the AWAY command handler, s_user.c:2702) + `send_away` (the RPL_AWAY emitter, s_user.c:3547) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (two new `#ifndef PORT_USER_P5` guard regions; the cref oracle keeps the full unguarded `s_user.o`). **Pure handler cluster** (AWAY→`m_away` is a `msgtab` row) — L2 golden is the gate, RED confirmed via undefined `m_away`/`send_away`; no parse.rs edit. `send_away` was previously imported from `ircd_sys::bindings` and called by the already‑ported `m_message`/`m_whois`; **removing it from the import list and defining it locally makes those two call sites resolve to Rust** (it's still `#[no_mangle] pub extern "C"` since it's an `s_user_ext.h` symbol). Plan: `docs/superpowers/plans/2026-06-02-p5g-s_user-away.md`.
  - **Config that shaped the port (verified in `cbuild/config.h`):** `USE_SERVICES` **off** → `m_away`'s two `#ifdef USE_SERVICES check_services_butone` blocks not compiled/not ported. **`AWAY_MOREINFO` defined** (config.h:580) → `send_away`'s else branch (away == NULL) is the `":%s 301 %s %s :Gone, for more info use WHOIS %s %s"` form, **not** the `#else` RPL_AWAY‑"Gone" — ported the `#ifdef AWAY_MOREINFO` branch. Both callers guard on FLAGS_AWAY ⇒ `away != NULL` in practice, so the live path is the `if (acptr->user->away)` RPL_AWAY‑with‑text branch (the AWAY_MOREINFO else is faithfully ported but effectively dead).
  - **Faithfulness note — the non‑MyConnect away‑buffer leak reproduced verbatim.** For a remote (`!MyConnect`) `sptr` marking away, C `MyMalloc`s the buffer into the local `away` but only assigns `sptr->user->away = away` (and `strcpy`s) inside the `if (MyConnect(sptr))` block — so the remote‑away allocation is leaked. The Rust port mirrors this exactly (allocate + set `FLAGS_AWAY` + the `+a` server propagation unconditionally; store + strcpy + 306 ack only under `my_connect`). Not "fixed" (P8 territory).
  - **Types/`format!`:** `istat.is_away`/`is_awaymem` are `u_long` (cast `(strlen+1)`/`len` to `u_long`); `MyMalloc`/`MyRealloc` (P2 Rust ports) take `usize` and return `*mut c_char` directly (no cast); `MyFree`→libc `free`; `(*user).uid` is `[c_char;10]` → passed via `.as_mut_ptr()`; `TOPICLEN`=255. `format!` is **N/A** — both fns hand every reply to a C variadic sender (`sendto_one`/`sendto_serv_butone`) as a format string + raw pointer/C‑string args (the sender formats per‑recipient); the only literals are the `c"…"` format strings (documented in‑module, matching the P5f note). No integer conversions through the senders → the P5c `%lu`‑width hazard doesn't apply.
  - **Verification:** L2 `golden_s_user_away` 3/3 byte‑identical: **AWAY :gone fishing / AWAY** (306 RPL_NOWAWAY then 305 RPL_UNAWAY — both `m_away` arms), **WHOIS alice while away** (`send_away` → `301 alice alice :brb` inside the WHOIS block; 317 idle/signon masked by the existing canonicalizer), **bob PRIVMSG alice while away** (two clients → bob gets `301 bob alice :brb` via `send_away` from the Rust `m_message`). `m_away`/`send_away` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (all 12 `*_diff` L1 + layout + all 8 golden suites); 0 warnings. No new canonicalizer masks needed. **Deferred to S2S L2:** the `:%s MODE %s :±a` server‑burst propagation (no server link under `‑s`/`‑p standalone`; ported faithfully, covered by review).
- **2026‑06‑02 — P5h (s_user.c UMODE cluster) merged.** `m_umode` (s_user.c:3164), `send_umode` (:3358), `send_umode_out` (:3409) + the `user_modes[]` table (:38) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (one new `#ifndef PORT_USER_P5` region spans the three fns; the `user_modes[]` static is guarded separately since only this cluster uses it). **Handler cluster** (`UMODE`→`m_umode` is a msgtab row; `MODE <nick>` routes through still‑C `m_mode` (channel.c) → Rust `m_umode` once the target isn't a channel) — L2 golden is the gate. RED confirmed via undefined `m_umode`/`send_umode`/`send_umode_out`; no parse.rs edit. Plan: `docs/superpowers/plans/2026-06-02-p5h-s_user-umode.md`.
  - **`send_umode` is called from three still‑C TUs** (`register_user` in s_user.c, `s_service.c`, `s_serv.c` — all via `send_umode(NULL, …, buf)`); porting it means **those C callers now resolve to the Rust def at link**, so byte‑faithfulness is load‑bearing beyond just `m_umode`. This is why `send_umode` gets its own L1 differential (`s_user_umode_diff`): the +/- mode‑diff string builder with `cptr=NULL` is socket‑free, so it's L1‑testable directly (10 cases over old/current‑flags/sendmask/MyClient combos — pure/grouped/mixed add‑del/`SEND_UMODES` vs `ALL_UMODES` filtering).
  - **Config (verified `cbuild/config.h`):** `USE_SERVICES` **off** → m_umode's three `check_services_butone` blocks (away‑clear, oper‑±o, umode propagation) and send_umode_out's trailing block are not compiled/not ported. The active `MODE_*` values for s_user are `MODE_NULL=0`/`MODE_ADD=0x40000000`/`MODE_DEL=0x20000000` (struct_def.h:876‑878; the 750‑758 block is commented out) — bindgen agrees. `user_modes[]` resolves to `{(OPER,'o'),(LOCOP,'O'),(INVISIBLE,'i'),(WALLOP,'w'),(RESTRICT,'r'),(AWAY,'a')}`; `SEND_UMODES=61`, `ALL_UMODES=63`.
  - **`format!` per user request:** m_umode's **query reply** ("+<modes>", pure ASCII) is built with a Rust `String` + `CString` (the format!‑family path), then handed to the C variadic `sendto_one` as the `replies[RPL_UMODEIS]` `%s`. `send_umode` writes its +/- diff into the **caller‑owned** `*mut c_char` buffer with the add/del grouping algorithm — that cannot be a `String` (the bytes must land in the C buffer, no owned allocation), so it stays a faithful byte‑builder. No integer conversions reach the senders → the P5c `%lu`‑width rule is N/A. Documented in‑module.
  - **Faithfulness notes:** the 'a' switch case **falls through** to default for server/internal callers (`cptr==NULL || IsServer(cptr)`) — reproduced by the `_ =>` arm doing the 'a'‑specific away‑clear then continuing into the flag‑flip; a local client's `+a` is a no‑op (`continue`, the C switch `break`). The misleading‑indent `return 1;` after the USERSDONTMATCH `else` is **inside** the mismatch‑`if` block (unconditional once the guard trips) — reproduced. Inner loop advances the char pointer **first** so `continue` = C's switch `break`. ClearOper/ClearLocOp also set `status=STAT_CLIENT`; SetClient = `IsAnOper?STAT_OPER:STAT_CLIENT` — reproduced. The private `UMODE_BUF` static replaces the s_user.c file‑static `buf` (transient within send_umode_out; the C remnant keeps its own `buf`). `istat.is_user`/`is_oper`/`is_awaymem` are `u_long`; `servp->usercnt` is `[c_int;3]`.
  - **Verification:** L1 `s_user_umode_diff` zero‑diff vs `cref_send_umode` (10 cases). L2 `golden_s_user_umode` 2/2 byte‑identical: **MODE alice +i ; MODE alice** (echo `:alice MODE alice :+i` via the ALL_UMODES local echo + `221 alice +i`), **MODE alice +iw-i ; MODE alice** (parse nets to +w / ‑i → echo `:alice MODE alice :+w` + `221 alice +w`, exercising the +/- grouping). `m_umode`/`send_umode`/`send_umode_out` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (all `*_diff` L1 + layout + all 9 golden suites); 0 warnings. No new canonicalizer masks. **Deferred to S2S L2:** the `send_umode_out` server‑broadcast loop (`fdas`/`local[]` → `:uid MODE name :modes`) and the oper/restricted (`det_confs_butmask`, ERR_RESTRICTED) paths — need a server link / oper; ported faithfully, covered by review + the L1 differential.
- **2026‑06‑02 — P5i (s_user.c PING/PONG cluster) merged.** `m_ping` (s_user.c:2781) + `m_pong` (s_user.c:2818) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (one new `#ifndef PORT_USER_P5` region spans both; the cref oracle keeps the full unguarded `s_user.o`). **Pure handler cluster** (PING→`m_ping`, PONG→`m_pong` are both msgtab rows, col 1) — L2 golden is the gate. RED confirmed via undefined `m_ping`/`m_pong`; no parse.rs edit. Plan: `docs/superpowers/plans/2026-06-02-p5i-s_user-ping.md`.
  - **Cleanest cluster yet — pure routing, no statics.** Both handlers call only external lookup/send fns (`find_client`/`find_server`/`find_target`/`match`/`mycmp`/`sendto_one`); **no file‑statics, no allocators, no list linkage**, so the shared C remnant (`buf`/`buf2`/`user_modes`) is untouched and there are **no socket‑free data ops** → **no L1** (like P5e/P5g). New bindings imports: `find_target`/`FLAGS_PINGSENT`/`ERR_NOSUCHSERVER`/`ERR_NOORIGIN`; reused the existing `is_me` helper (s_user.rs:905).
  - **`format!` per user request — N/A, documented.** Both fns hand every reply to a C variadic sender (`sendto_one`) as a format string + raw pointer/C‑string args (no intermediate string to build); the only literals are the `c"…"` format strings / `replies[]` numerics. No integer conversions reach the senders → the P5c `%lu`‑width rule is N/A.
  - **Faithfulness notes:** m_ping reassigns the local `origin` (find miss → `cptr->name`) but the else‑branch PONG still echoes the **original** `parv[1]` (verbatim C); `match(destination, ME) != 0` = destination does **not** match the server name. m_pong clears `FLAGS_PINGSENT` (`flags` is `c_long`) before the target lookup; the `&me` no‑destination default uses `addr_of_mut!(me)`.
  - **Verification:** L2 `golden_s_user_ping` 2/2 byte‑identical: **`PING :hello`** → `:irc.test PONG irc.test :hello` (the find‑miss → else echo path), **`PING alice noserver.invalid`** → `402 ... :No such server` (the `find_server` miss). `m_ping`/`m_pong` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (all `*_diff` L1 + layout + all 10 golden suites); 0 warnings. No new canonicalizer masks. **No L2 path for `m_pong`** (clears `FLAGS_PINGSENT`; only emits on the `!IsMe` forward path → needs a server/target link) — ported faithfully, covered by review.
- **2026‑06‑02 — P5j (s_user.c OPER cluster) merged.** `m_oper` (s_user.c:2866) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (one new `#ifndef PORT_USER_P5` region around `m_oper`; the cref oracle keeps the full unguarded `s_user.o`). **Handler cluster** (OPER→`m_oper` is a msgtab row, client column) — L2 golden is the gate. RED confirmed via undefined `m_oper` (no other symbol; it has no file‑statics of its own under this config). No parse.rs edit. **Reuses the already‑Rust `send_umode_out`** (P5h) for the grant echo. Plan: `docs/superpowers/plans/2026-06-02-p5j-s_user-oper.md`.
  - **Config that shaped the port (verified `cbuild/config.h`):** `CRYPT_OPER_PASSWORD` **off** (#undef:311 → `encr = password`, no crypt), `FNAME_OPERLOG`/`USE_SYSLOG`/`FAILED_OPERLOG` (#undef:300)/`UNIXPORT`/`USE_SERVICES` **off**, `NO_OPER_REMOTE` **off** (#undef:555 → the `#ifndef` arm: `if (aconf->flags & ACL_LOCOP) SetLocOp else SetOper`). ⇒ the trailing `logstring`/syslog/logfile block is an **empty block → omitted entirely**; the resolved body is ~40 lines. New helpers added: `set_oper`/`set_locop` (flags |= FLAGS_OPER/LOCOP; status = STAT_OPER), mirroring the existing `clear_oper`/`clear_locop`. Via bindings: `find_Oline`/`attach_conf`/`detach_conf`/`aConfItem`/`CONF_OPS`/`ACL_LOCOP`/`RPL_YOUREOPER`/`ERR_NOOPERHOST`/`ERR_PASSWDMISMATCH`/`ServerChannels_SCH_NOTICE`; `strcmp` (StrEq) + `sendto_flag` added to the local extern block.
  - **Faithfulness notes:** `encr = parv[2]` may be NULL (OPER min‑params is 2) but is only dereferenced past a `find_Oline` hit, so the wrong‑name 491 path is safe; `StrEq` reproduced via libc `strcmp` (same UB as C if a matching O‑line is hit with no password — not exercised). The transient host split `s = index(host,'@'); *s='\0'; …; *s='@'` is reproduced verbatim even though `s` (after‑'@') is unused under NO_OPER_REMOTE‑off (net no‑op; mutates the C‑owned host in place and restores it). `istat.is_oper` is `u_long`; `servp->usercnt` is `[c_int;3]` → `usercnt[2] += 1`. `%d`/`%c` args (the `:%s %d %s :Too many …` lines + the SCH_NOTICE operator‑letter) cast to `c_int` (the P5c width rule).
  - **`format!` per user request — N/A, documented.** Every reply is handed to a C variadic sender (`sendto_one`/`sendto_flag`) as a format string + raw pointer/`%d`/`%c` args (the sender formats per‑recipient); there is no intermediate string to build under this config (the only `sprintf(buf,…)` lives in the compiled‑out FNAME_OPERLOG block). Documented in‑module, matching the P5f/g/i notes.
  - **L2 harness gained `boot_conf(binary, args, conf)`** (refactor of `boot_args`, which now calls it with `minimal.conf`) so a test can use a dedicated config; new **`oper.conf`** = minimal.conf + a full‑operator O‑line `O|*@*|secret|alice|0|10|` (uppercase O → no ACL_LOCOP → SetOper; class 10 exists), isolated from every other golden test.
  - **Verification:** L2 `golden_s_user_oper` 1/1 byte‑identical over three OPER attempts on one registered client: **`OPER nope wrong`** → `491 ERR_NOOPERHOST` (find_Oline miss), **`OPER alice wrong`** → `464 ERR_PASSWDMISMATCH` (StrEq fails → detach path), **`OPER alice secret`** → success: `:alice MODE alice :+o` (send_umode_out ALL_UMODES local echo) + `381 RPL_YOUREOPER`. `m_oper` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (67 passed, 0 failed: all `*_diff` L1 + layout + all 11 golden suites); 0 warnings. No new canonicalizer masks (no time tokens). **Deferred to S2S L2:** the `send_umode_out` server‑broadcast loop, the LocOp (`o`) path, and the SCH_NOTICE to server‑notice subscribers — need a server link / a subscribed oper; ported faithfully, covered by review.
- **2026‑06‑02 — P5k (s_user.c QUIT/POST exit cluster) merged.** `m_quit` (s_user.c:2479) + `m_post` (s_user.c:2467, the http‑proxy‑abuse alias) ported into `ircd-common/src/s_user.rs`, extending the existing `s_user_link.o` partial port (one new `#ifndef PORT_USER_P5` region around s_user.c:2466‑2498; the cref oracle keeps the full unguarded `s_user.o`). **Pure handler cluster** (QUIT→`m_quit`, POST→`m_post` are both msgtab rows) — L2 golden is the gate. RED confirmed via undefined `m_quit`/`m_post`; no parse.rs edit. Both handlers terminate in the **already‑Rust `exit_client`** (P5c). Plan: `docs/superpowers/plans/2026-06-02-p5k-s_user-quit.md`.
  - **Cleanest cluster after PING/PONG — self‑contained, one tiny static.** `m_quit`'s only state is its function‑local `static char comment[TOPICLEN+1]`, reproduced as the private module static `QUIT_COMMENT` (not ABI); `m_post` has no state. They share **no** file‑statics with the C remnant (`buf`/`buf2`/`user_modes` untouched). `m_post` tail‑calls `m_quit` after a `SCH_LOCAL` server‑notice. New bindings imports: `exit_client`/`get_client_host`/`ServerChannels_SCH_LOCAL`; reused `is_server`/`my_connect` helpers.
  - **Columns `[server, client, oper, service, unreg]` decide reachability:** QUIT = `[m_quit ×5]` → a **registered** client (col 1) hits `m_quit`; POST = `[m_nop,m_nop,m_nop,m_nop,m_post]` → only col 4 (**unregistered**) hits `m_post`. So the two L2 scenarios use different client states (registered alice QUIT; raw unregistered connection POST). No clean socket‑free data op (both end in `exit_client`, which closes the connection) → **no L1**, like P5e/g/i.
  - **`format!` per user request — N/A for the comment (documented).** `m_quit`'s `comment` is built from `parv[1]`, an arbitrary possibly‑non‑UTF‑8 C‑string under C `snprintf` truncation at `TOPICLEN`; per the standing P5c rule that can't pass through a UTF‑8 `String`/`format!` without corruption → it's a faithful byte‑builder (`snprintf_prefix_str` emulates `snprintf(buf,size,"<prefix>%s",s)` truncation; the MyConnect path then `strcat`s the closing `"` exactly as C). `m_post`'s notice is handed straight to the C variadic `sendto_flag` (no intermediate string). No integer conversions reach the senders → the P5c `%lu`‑width rule is N/A.
  - **Faithfulness notes:** `IsServer(sptr)` → return 0; the MyConnect path is `snprintf(comment, TOPICLEN, "\"%s", parv1)` then `strcat(comment, "\"")` (= `"` + parv1 + `"`), the remote path is `snprintf(comment, TOPICLEN+1, "%s", parv1)` (raw, no quotes — reproduced even though it needs an S2S link to reach); `parv[1]` is `NULL`‑guarded to `""` exactly as C (`(parc>1 && parv[1]) ? parv[1] : ""`).
  - **Verification:** L2 `golden_s_user_quit` 2/2 byte‑identical: **`QUIT :gone fishing`** (registered) → `ERROR :Closing Link: alice[~alice@127.0.0.1] ("gone fishing")`, **`POST`** (unregistered) → the `020` connection notice + `ERROR :Closing Link: [unknown@127.0.0.1] ("")`. `m_quit`/`m_post` defined `T` from ircd‑common in the Rust `ircd`; full `cargo test` green (all `*_diff` L1 + layout + all 12 golden suites); 0 warnings. No new canonicalizer masks (no time tokens). **Deferred to S2S L2:** the remote (`!MyConnect`) QUIT branch (needs a server link; ported faithfully, covered by review).

