diff --git a/issues/a-shared-boot-stopped-answering-and-no-capture-says-why.md b/issues/a-shared-boot-stopped-answering-and-no-capture-says-why.md
index d31764eb55f..3beb8d70966 100644
--- a/issues/a-shared-boot-stopped-answering-and-no-capture-says-why.md
+++ b/issues/a-shared-boot-stopped-answering-and-no-capture-says-why.md
@@ -46,8 +46,8 @@ shared boot waiting for its next command is parked at almost every sample.
`SYS_THREAD_JOIN` whose wake was lost: it parks on the target thread's own watch
at `Deadline::never()` (`kernel/src/syscall/proc.rs:171`), and nothing else
would leave a whole boot idle straight after a sibling thread's clean exit.
-`issues/thread-exits-completion-post-is-the-second-one.md` owns that
-path's two posts.
+That wake is the one post `process::thread_exit` makes on the thread's own
+watch before its exit pass.
**Exit condition.** A capture taken from a boot that has actually stopped, which
names the first waiter and the subject it waits on — that boot's trace diary
diff --git a/issues/std-net-maps-every-other-syscall-error-to-other.md b/issues/std-net-maps-every-other-syscall-error-to-other.md
new file mode 100644
index 00000000000..012fc2a8615
--- /dev/null
+++ b/issues/std-net-maps-every-other-syscall-error-to-other.md
@@ -0,0 +1,37 @@
+---
+status: open
+kind: defect
+opened: 2026-09-01
+---
+
+# std's net backend still maps every other `SyscallError` to `ErrorKind::Other`
+
+Making a broken pipe reach a Rust caller as `BrokenPipe` put one exhaustive
+`SyscallError -> ErrorKind` map in `sdk/std/sys/pal/mod.rs` and pointed
+`sys/pipe`, `sys/stdio` and `sys/fs` at it. `sdk/std/sys/net/connection.rs`
+keeps a map of its own with a `_ => Other` arm:
+
+```rust
+fn syscall_err(e: SyscallError) -> io::Error {
+ match e {
+ SyscallError::WouldBlock => io::ErrorKind::WouldBlock.into(),
+ _ => io::Error::new(io::ErrorKind::Other, "syscall error"),
+ }
+}
+```
+
+A TCP stream no longer goes through it: `TcpStream` reads and writes through
+`toyos::net::stream`, whose refusals `stream_refused` maps by name. What still
+does is `UdpSocket`'s `recv_from` and `send_to`, which read and write the
+socket's pipes to netstack, and the listener's notification reads. So a `UdpSocket::send_to` after netstack has gone
+answers `ErrorKind::Other` where the pipe path answers `BrokenPipe`, by reading
+the code, and a new `SyscallError` variant is absorbed there silently rather
+than failing to compile until somebody decides its kind.
+
+## What closing it takes
+
+Pointing `syscall_err`'s callers at `sys::to_io_error` and deleting the local
+map, as the three modules already moved were; and a test that a datagram
+socket's write to a departed netstack answers `BrokenPipe`, against libc's
+`sendto` on the same shape. `netstack_gone_mid_bind` is the boot that already
+stages a departed netstack.
diff --git a/issues/suite-rows-that-are-measurements-or-make-several-claims.md b/issues/suite-rows-that-are-measurements-or-make-several-claims.md
index 80fb18b9ad7..44c3ae1be53 100644
--- a/issues/suite-rows-that-are-measurements-or-make-several-claims.md
+++ b/issues/suite-rows-that-are-measurements-or-make-several-claims.md
@@ -32,7 +32,6 @@ Guest binaries in `tests/guest/rust-tests/src/bin/` that make several claims:
- `std_process.rs`: eight std process behaviours.
- `std_sync.rs`: mutex, condvar, park and rwlock.
- `sched_stress.rs`: io_uring acceptor isolation, the post-wake vruntime lag, and a connect storm.
-- `toybox_file_tools.rs`: `cp`, `mv` and `hexdump`.
- `std_fs.rs`: listing, a self-read, NotFound, and file types.
Owner: the orchestrator, which dispatches the reshape.
diff --git a/issues/t14-desktop-froze-at-64s.md b/issues/t14-desktop-froze-at-64s.md
index 81b6070721d..7aa5a11c559 100644
--- a/issues/t14-desktop-froze-at-64s.md
+++ b/issues/t14-desktop-froze-at-64s.md
@@ -57,7 +57,7 @@ ruled it out.
**What the next boot should capture.** Nothing new, which is the point: the
compositor now names every client it drops and why. A recurrence with no
-`compositor: dropping pid` line and no telemetry is a mechanism none of the
+`compositor: dropping client` line and no telemetry is a mechanism none of the
four closed ones covers, and that is itself the finding.
**Nothing replaces the 2 s report as a heartbeat.** It left the log (#891),
diff --git a/issues/t14-keyboard-will-not-report-its-scancode-set.md b/issues/t14-keyboard-will-not-report-its-scancode-set.md
deleted file mode 100644
index f97644e7a09..00000000000
--- a/issues/t14-keyboard-will-not-report-its-scancode-set.md
+++ /dev/null
@@ -1,82 +0,0 @@
----
-status: open
-kind: defect
-opened: 2026-08-02
----
-
-# The T14's keyboard will not report its scancode set, and one byte reached no event
-
-The boot after the FADT gate came out reached the keyboard and stopped one step
-from the end:
-
-```
-i8042: ok selftest=0x55 cfg=0x77->0x64 port1=ok port2=ok
-i8042: kbd cmd 0x02 answered Some(238), not ack
-i8042: kbd refused scancode set 2 ... disabled
-```
-
-238 is `0xEE`, ECHO's own reply, returned for the **argument** of `0xF0 0x02`
-after the command byte was acked and after `0xF5` had been acked — so the EC
-answers commands and does not implement this one. The driver now reads the set
-rather than writing it, and where the read is refused it decides the wire format
-from the translate bit firmware left in the config byte (`0x77` on this
-machine), which is exactly what Linux's `i8042.c`/`atkbd` do and all they do on
-a portable device. `i8042_kbd_echo` gates it.
-
-**The boot after that one worked**, and it is the first time any of this has run
-on the metal it was written for:
-
-```
-i8042: kbd set2+xlat (assumed, the set query was refused) scanning on, GSI 1 -> vec 0x24 apic 0 on
-i8042: aux rate=100 res=8/mm, GSI 12 -> vec 0x24 apic 0
-i8042: armed at 1460ms, idle at 3394ms, 0 interrupts ... the pin has never asserted (kbd GSI 1, aux GSI 12)
-i8042: the pin asserts ... 1 interrupts, 1 bytes, 0 keys, 0 motion, first seen at 11375ms
-```
-
-The driver attaches; the **aux port initialises fully** — `rate=100 res=8/mm`
-means the TrackPoint answered its whole reset/id/rate/resolution sequence, which
-no previous boot reached because every keyboard-side refusal returns before that
-block; and a physical keypress at 11375 ms raised a real interrupt on GSI 1, so
-the routing, the RTE programming, the vector and the unmask are all correct on
-Tiger Lake silicon. `Boot: peripherals ready` went from 6 ms to 398 ms in the
-same boot: that is the aux reset stage now actually running against a device
-that takes real time, not a regression.
-
-What is open:
-
-- **One byte reached the kernel and produced no event, and the counters could
- not say which byte.** That is the open item, and half of it is the
- instrument. Enumerated against the real tables (`kernel/ps2/src/key.rs`),
- **84 of the 256 single byte values decode to nothing** under set 1: both
- prefixes (`0xE0`, `0xE1`), the two `Lost` codes, and every unmapped slot —
- `0x54`, `0x55`, `0x59`–`0x80` and their break forms. `handle_key` drops a
- break for a usage nothing held, which adds `0xAA` (left Shift's break under
- translation, and a keyboard announcing a reset). So `1 bytes, 0 keys` covers
- an extended key where **nothing is wrong**, a late `0xFA`, another `0xEE`, a
- device reset, and a wire carrying raw set 2 — where Enter is `0x5A`,
- Backspace `0x66`, Escape `0x76` and 23 such codes land on unmapped slots.
- Only the byte separates them. The driver now records the bytes that produced
- no event and names them in the health line, and says it a second time if a
- later byte does decode; `i8042_undecoded_bytes` gates both. The next diag
- boot answers this in one line without a reflash.
-- **The wire format is still the `assumed` one, so a raw-set-2 wire is among
- the suspects.** It is not the likeliest — a mismatch would usually produce
- *wrong* events rather than none, since most set-2 codes do land on a mapped
- set-1 slot — but 23 of them do not, and the byte value is what settles it.
-- **The fallback's evidence is firmware's intent, not a read-back.** `before &
- CFG_TRANSLATE` says firmware enabled a set2→set1 translator; that it did so
- for a device emitting set 2 is inference, tight but inference. The success
- line says `(assumed, the set query was refused)` rather than `(readback
- 0x41)` precisely so the panel does not claim otherwise. A machine where the
- inference is wrong types nonsense, which is the outcome the read-back exists
- to prevent — there is no third instrument for it on this wire.
-- **`0xF2` is not that instrument.** A translating controller answers the MF2 id
- `AB 83` as `AB 41` (`translate_table[0x83] == 0x41`, QEMU `hw/input/ps2.c`;
- QEMU's own keyboard hardcodes the same pair), which would prove the translator
- is live on the data path and not merely enabled in a bit. It is not sent,
- because Linux's `atkbd_skip_getid` withholds `0xF2` from every translated
- portable device — "on many modern laptops ATKBD_CMD_GETID may cause problems"
- — and the T14 is one. Sending a command Linux avoids on this exact machine
- class to shore up an inference is the wrong trade.
-
-`i8042_undecoded_bytes` is deleted; `issues/parallel-tests-red-under-other-suites.md` records the commit that restores it.
diff --git a/issues/t14-lost-every-integrated-input.md b/issues/t14-lost-every-integrated-input.md
index 3490312200c..88a5bd2ad43 100644
--- a/issues/t14-lost-every-integrated-input.md
+++ b/issues/t14-lost-every-integrated-input.md
@@ -24,17 +24,22 @@ packets — `0x08` is a resting head and `0x06` is a `dx` of +6. **The pointer w
framing perfectly.** The arithmetic is forced and no reader would do it.
Closed, therefore: the decoder did not desync, and no fix for a desync was
-needed. What was wrong is the instrument, and it is now fixed (`647c3c0`,
-`toyos-ps2`) — `MouseOutcome` could not distinguish a byte held inside a packet
-from a byte thrown away at a boundary, so two of every three bytes of a healthy
-pointer stream were reported as suspects. `i8042_mouse` now runs three thousand bytes of
-clean packets and requires the driver to name none of them; reverting the split
-reds it with the T14's own line shape.
+needed. What was wrong is the instrument, and it is fixed (`647c3c0`) —
+`MouseOutcome` could not distinguish a byte held inside a packet from a byte
+thrown away at a boundary, so two of every three bytes of a healthy pointer
+stream were reported as suspects. `toyos-ps2`'s
+`a_healthy_stream_discards_nothing_and_leaves_no_byte_unaccounted`
+(`system/ps2server/ps2/tests/decode.rs`) holds that with the T14's two packets.
+
+The kernel driver that logged this session is gone: `/system/bin/ps2server`
+drives the i8042 from userland (#899) and resets both devices itself
+(`system/ps2server/src/probe.rs`). Nothing since has read a T14 session long
+enough to see whether the loss recurs under it.
**What remains open is the actual question, and the log cannot answer it.**
The tally counts in the ISR before any decoding, so 6 interrupts is hardware truth
-— but it is truth *as of 6.609 s*, which is when the driver stopped speaking.
-`HEALTH_DONE` was terminal. For the remaining 54 s the log cannot separate:
+— but it is truth *as of 6.609 s*, which is when the driver stopped speaking:
+that driver's health line was said once. For the remaining 54 s the log cannot separate:
- **the pin stopped asserting** — a wedged controller, a lost edge, an EC that
stopped scanning, an RTE that got masked; from
@@ -48,16 +53,17 @@ keyboard produced no byte at all in 58 s** — not "stopped at 6.6 s", never. Th
same machine's earlier boots drove a shell off that keyboard
(`c7efcd30d^:specs/reference/metal-hardware-inventory.md`), so it is not a routing fault.
-The cadence fix is what makes the next session decisive rather than a guess:
-after the verdict the counters repeat, at most once per 10 s and **only when the
-pin has asserted since the last line**. That gating is the point — past the
-first repeat, no line means no interrupt, so silence becomes evidence instead of
-absence of evidence.
+ps2server's counters line is what makes the next session decisive rather
+than a guess: it repeats at most once per 10 s and **only when an interrupt has
+come since the last line** (`Stream::counters`, `system/ps2server/src/stream.rs`).
+Past the first line, no line means no interrupt, so silence becomes evidence
+instead of absence of evidence.
-**What the next boot should capture.** A repeat line dated after 6.6 s, or none.
-If bytes are arriving, `undecoded`/`discarded` name the fault in this driver. If
-no line appears at all, the pin is not asserting and the next suspect is the
-controller or the EC — and nothing in `toyos-ps2` can be responsible.
+**What the next session should capture.** A `ps2server:` counters line dated
+after the inputs go dead, or none. If bytes are arriving, `undecoded` and
+`discarded` name the fault in the decoder. If no line appears at all, the pin is
+not asserting and the next suspect is the controller or the EC — and nothing in
+`toyos-ps2` can be responsible.
Two things deliberately not concluded:
@@ -67,15 +73,7 @@ Two things deliberately not concluded:
`0xF2` with id `0x00` — a plain 3-byte mouse — so the driver's 3-byte frame is
what the wire carries, and the 4-byte IntelliMouse mismatch usually blamed for
a PS/2 desync is not available here.
-- **The USB mouse plugged in at 30.4 s produced no motion either**, which is the
- xHCI HID completion-requeue item in this section and not this one.
-
-The endowment table stays indistinguishable here even after the presence gate
-landed (#342): on this machine the i8042 is present and both claims still mint
-whether the devices answer or not — the gate keys `declare_source()` on
-`aux_line.is_some()` / the GSI existing, while `unmasked`/`aux_unmasked`, both
-computed one line above (i8042/mod.rs:1320,1323), go unread. Gating the two
-declarations on those is the one-line fix direction the #342 review named; it
-needs this machine to prove.
+- **The USB mouse plugged in at 30.4 s produced no motion either**, which is
+ `issues/the-t14-mouse-may-be-another-defect.md` and not this one.
`i8042_mouse` is deleted; `issues/i8042-mouse-ends-four-packets-short-with-a-clean-exit.md` records the commit that restores it.
diff --git a/issues/t14-rows-hold-toyos-to-linuxs-choice-or-copy.md b/issues/t14-rows-hold-toyos-to-linuxs-choice-or-copy.md
index b6cb542d86e..034ac13a849 100644
--- a/issues/t14-rows-hold-toyos-to-linuxs-choice-or-copy.md
+++ b/issues/t14-rows-hold-toyos-to-linuxs-choice-or-copy.md
@@ -11,11 +11,12 @@ setting must be, and its copy of a fact a primary source states is never an orac
rows in `tests/toyos.rs` used one. `counters_on_metal`'s, the request Linux's governor chose,
went in #915; the other remains:
-- `acpi_tables_on_metal` holds the number of definition blocks the server loaded to
- `T14_DEFINITION_BLOCKS`, the 14 Linux reports loading. The firmware's XSDT and FADT, which the
- kernel already reads, are the primary source of that count.
+- `acpi_tables_on_metal` holds the number of definition blocks the server loaded to a
+ constant per bench, `Bench::blocks` in `tests/toyos.rs`: the T14's 14, the count Linux
+ reported loading, and the AMD bench's 17. The firmware's XSDT and FADT, which the kernel
+ already reads, are the primary source of that count.
Owner: the orchestrator, which dispatches the reshape.
**Exit condition**: `acpi_tables_on_metal` holds the count to the definition blocks
-the same boot's firmware tables list, and `T14_DEFINITION_BLOCKS` is deleted.
+the same boot's firmware tables list, and `Bench::blocks` is deleted.
diff --git a/issues/test-runner-holds-logread-where-no-log-builtin-runs.md b/issues/test-runner-holds-logread-where-no-log-builtin-runs.md
index 6efe8df1685..b45e39c11d5 100644
--- a/issues/test-runner-holds-logread-where-no-log-builtin-runs.md
+++ b/issues/test-runner-holds-logread-where-no-log-builtin-runs.md
@@ -6,20 +6,21 @@ opened: 2026-09-28
# test-runner holds `logread` where no log builtin runs
-`logread` in a `[programs.test-runner]` row is authority for the builtins that
-read the log inside test-runner (`log-gate`, `log-storm`, `log-close`,
-`tests/guest/test-runner/src/main.rs`'s `BUILTINS`). Every boot that runs one is a
-`tests/images/testcases.toml` boot (`tests/common/logread.rs` boots the machine tests' config;
-`tests/toyos.rs`'s one job list naming `log-close` is `tests/images/testcases.toml`). Seven
-other manifests grant it anyway: `tests/partclaimcase`, `tests/blockdcase`,
-`tests/doommusiccase`, `tests/logrotatecase`, `tests/images/metalcase.toml`, `tests/images/netcase.toml`
-and `tests/sshdcase`.
+test-runner has no builtin that reads the log any more: `log-gate`,
+`log-storm` and `log-close` are gone from `tests/guest/test-runner`. Its
+`logread` is used only by the children it hands a `SysCap` duplicate to, and
+it hands one only where its row also grants `dup` (`run_one`,
+`tests/guest/test-runner/src/main.rs`).
-`partclaimcase` and `blockdcase` also grant `dup`, so there every child
-test-runner spawns receives a `SysCap` duplicate carrying `LOG` as well.
+- `tests/images/testcases.toml`'s row grants `dup` beside `logread`, and its
+ children `disk_backtrace` and `inbox_log_post` read the log through the
+ duplicate. Its comment still gives "the log gate" as the reason.
+- `tests/images/metalcase.toml`'s row grants `logread` and no `dup`, so no
+ child receives it and test-runner reads no log itself: nothing on that boot
+ uses it.
-**Evidence:** `rg -n '"log-gate"|"log-storm"|"log-close"' tests userland`, and
-`rg -n logread -g system.toml tests`.
+**Evidence:** `rg -n logread tests/images` and `rg -n 'log_read|cursor_words'
+tests/guest`.
**Exit:** every `logread` in a test manifest is one a program on that boot
-uses, or the row says which use it is for.
+uses, and the row's comment says which use it is for.
diff --git a/issues/the-ack-delay-abi-doc-names-one-cpu.md b/issues/the-ack-delay-abi-doc-names-one-cpu.md
deleted file mode 100644
index 378310c8b8c..00000000000
--- a/issues/the-ack-delay-abi-doc-names-one-cpu.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-status: open
-kind: defect
-opened: 2026-09-14
----
-
-# `TLB_ACK_DELAY_ARM`'s ABI doc still names one CPU
-
-`sdk/abi/src/syscall.rs`'s `debug_action::TLB_ACK_DELAY_ARM` reads
-
-> Make the last CPU a shootdown waits for answer `arg` nanoseconds late, and
-> take it away again.
-
-The kernel arm no longer picks a CPU by arithmetic. `kernel/src/arch/x86_64/tlb.rs`'s
-`debug_arm_ack_delay` holds each other CPU's acknowledgement back for `arg`
-nanoseconds in turn, takes one shootdown against each, and returns the smallest
-wait any of them cost the initiator; the arming is then left standing against
-every other CPU until a disarm or the end of a fresh `ARM_WINDOW_NANOS`
-(`kernel/src/arch/x86_64/tlb.rs:230`, two seconds), whichever comes first. So the one
-sentence userland reads to learn what action 12 does describes a selection the
-kernel does not make, omits the answer it returns, and says nothing about how
-long what it leaves behind lasts.
-
-`sdk/abi/src` is one of `toolchain::SYSROOT_SOURCES`, so the correction is a
-single-commit branch of its own.
-
-**Exit condition.** The doc names what the kernel does: each other CPU in turn,
-the smallest of those waits returned, and the arming left standing against every
-other CPU until a disarm or the end of the two-second window, whichever comes
-first. A doc that names the arming without its window states a duration the
-kernel does not provide.
diff --git a/issues/the-acpi-servers-holder-drives-the-embedded-controller-unfiltered.md b/issues/the-acpi-servers-holder-drives-the-embedded-controller-unfiltered.md
index 3433d11867e..166b1f449c0 100644
--- a/issues/the-acpi-servers-holder-drives-the-embedded-controller-unfiltered.md
+++ b/issues/the-acpi-servers-holder-drives-the-embedded-controller-unfiltered.md
@@ -6,15 +6,18 @@ opened: 2026-10-04
# The ACPI server's holder drives the embedded controller unfiltered
-The `acpi` claim (`kernel/src/arch/x86_64/acpi_mode.rs`) opens the embedded
-controller's command and data ports to `/system/bin/acpiserver` through the
-TSS I/O permission bitmap, which grants a port or refuses it and cannot see
-the value written. Stage 1 sends the controller nothing but queries, but the
-same two ports take `WR_EC` (ACPI 6.5 §12.3), which writes any byte of the
-controller's space: on a laptop that is where fan control, charge thresholds
-and the controller's own thermal limits live. So a bug in that one program can
-reach whatever the machine's controller lets its host write, which the kernel
-never sees.
+The holder of the `acpi` claim, `/system/bin/acpiserver`, reaches the
+embedded controller's command and data ports through the kernel's mediated
+access, one byte each (`access`, `kernel/src/arch/x86_64/acpi_mode.rs`;
+`system/acpiserver/src/host.rs`). No row names those ports, and the fence
+(`toyos_userbound::firmware`) refuses a port only for whose it is, never for
+the value written. The server sends queries and, for the AML's accesses to
+the controller's space, `RD_EC` and `WR_EC` (`system/acpiserver/src/ec.rs`).
+`WR_EC` (ACPI 6.5 §12.3) writes any byte of the controller's space: on a
+laptop that is where fan control, charge thresholds and the controller's own
+thermal limits live. So a bug in that one program, or in the AML it runs, can
+reach whatever the machine's controller lets its host write, and the kernel
+makes the write without judging it.
The command port also takes any byte, and §12.3 defines five commands
(0x80–0x84); every other is the controller's maker's. Whether one of them puts
diff --git a/issues/the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it.md b/issues/the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it.md
index 7ecdd8f4ed4..a8e9e8098d8 100644
--- a/issues/the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it.md
+++ b/issues/the-boot-map-reaches-4-gib-and-firmware-decides-what-lands-in-it.md
@@ -11,10 +11,19 @@ scanout, and everything the kernel touches before `mm::init` has to be inside
it. The loader places none of it there: the kernel image
(`alloc_kernel_memory`), the memory map it hands over, the boot parameter and
ROOT's image all come from the pool allocator or `AnyPages`, wherever firmware
-puts them. `report_reach` then refuses the boot by name for the three it
-checks — and does not check the memory map at all, which the kernel reads
+puts them. `report_reach` (`loader/src/main.rs`) then refuses the boot by name
+for the kernel image, the parameter buffer, the microcode patch and the kernel
+arguments — and does not check the memory map at all, which the kernel reads
through the direct map before `mm::init` too.
+The memory map's buffer is sized and allocated after `loaderlog::close()`,
+because a console write, a FAT write and a handle drop can each add a
+descriptor, and `MAP_MARGIN` is fixed. Reporting the buffer means allocating
+it before its report line and the close, so those writes fall between the
+sizing and `ExitBootServices`. A `metal-sim` boot read the map's size before
+those writes and after the close and found zero added descriptors; the T14,
+whose FAT write goes to a real stick, has not been read.
+
On q35 OVMF allocates below 4 GiB, so the x86 suite has never met it. On QEMU
`virt` RAM starts at 1 GiB and AAVMF allocates from its top: with `-m 4G`
the kernel image lands at 0x13b400000 and the loader refuses the boot
@@ -27,8 +36,8 @@ the same defect.
**Exit condition**: the loader allocates what the kernel reads before
`mm::init` with `AllocateType::MaxAddress` inside the map (or the plan grows to
reach all of firmware's write-back memory), the memory map is among what
-`report_reach` checks, and `Profile::Virt` boots with the 4 GiB every other
-profile has.
+`report_reach` checks, its sizing margin read on the T14, and `Profile::Virt`
+boots with the 4 GiB every other profile has.
**The AArch64 typing refuses a part-typed page.** `Typing::of`
(`lib/bootmap/src/lib.rs`) refuses the boot (`Refusal::Mixed`) for any 2 MiB
diff --git a/issues/the-compiler-workspace-resolves-the-published-toyos-abi.md b/issues/the-compiler-workspace-resolves-the-published-toyos-abi.md
index d2bb3682010..07409c04b5a 100644
--- a/issues/the-compiler-workspace-resolves-the-published-toyos-abi.md
+++ b/issues/the-compiler-workspace-resolves-the-published-toyos-abi.md
@@ -10,10 +10,10 @@ Every bootstrap run is handed a `[patch]` of `toyos-abi` to the tree's
requirement accepts. The four forks `rust/Cargo.lock`
pins that name `toyos-abi` — getrandom `toyos-0.2` at 46592414 and `toyos-0.3`
at d3045447, libloading `toyos` at fa0abe77, stacker `toyos` at c25842ac — each
-require `"0.1"`, and the tree's crate is 0.16.0. So every bootstrap run
-re-locks `rust/Cargo.lock` onto `toyos-abi 0.1.0` from crates.io and records
-`[[patch.unused]] toyos-abi 0.16.0`; the committed lock still names the path
-package from when the tree's crate was 0.1.0. On a macOS or Linux host those
+require `"0.1"`, and the tree's crate is 0.16.0. So the committed lock (the
+`rust` fork at `a56b4057`) names `toyos-abi 0.1.0` from crates.io, and a
+bootstrap run handed the `[patch]` records `[[patch.unused]] toyos-abi
+0.16.0`. On a macOS or Linux host those
dependencies are `cfg(target_os = "toyos")` and never compiled; the
ToyOS-hosted rustc (`x86_64-unknown-toyos`) compiles them, so its next build
links getrandom, libloading and stacker against the published 0.1.0 ABI —
diff --git a/issues/the-compositor-ignores-a-message-it-does-not-know.md b/issues/the-compositor-ignores-a-message-it-does-not-know.md
index 14736ad821c..01cdb5fd485 100644
--- a/issues/the-compositor-ignores-a-message-it-does-not-know.md
+++ b/issues/the-compositor-ignores-a-message-it-does-not-know.md
@@ -12,12 +12,6 @@ dropped without a word, on a window's connection and on a fresh one alike. The
one retired type is refused by name;
every other unknown type is accepted and silently discarded.
-`compositor_stall`'s streaming case depends on it: its window sends
-`UNKNOWN_MSG` on every pass as load with nothing to draw
-(`tests/guest/rust-tests/src/bin/compositor_stall.rs`). A refusal there would
-drop the window after its first frame and leave the case passing with no load.
-
Owner: `Session::dispatch` in `system/compositor/src/session.rs`.
-**Exit**: an unknown type drops its client with `DropReason::OutOfProtocol`,
-and the stall's stream is a type the compositor serves without drawing.
+**Exit**: an unknown type drops its client with `DropReason::OutOfProtocol`.
diff --git a/issues/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md b/issues/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md
index 56c4725212b..3e1e7143eff 100644
--- a/issues/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md
+++ b/issues/the-deadline-header-promises-any-interrupt-and-only-the-timer-entry-polls.md
@@ -6,27 +6,22 @@ opened: 2026-09-16
# `deadline.rs`'s header promises the bound to any interrupt, and only the timer entry polls it
-`kernel/src/deadline.rs:11-13`: "So this one is armed off the parameter line
-and polled from the timer interrupt entry, on every CPU, in both rings. Its
-whole requirement is that some CPU still takes an interrupt".
-`kernel/src/hardlockup/mod.rs:4-5` repeats it: "`crate::deadline` is the bound
-on a *machine*, and its whole requirement is that some CPU still takes an
-interrupt: it is polled from the timer entry."
+`kernel/src/deadline.rs`'s header: "So this one is armed off the parameter
+line and polled from the timer interrupt entry, on every CPU, in both rings.
+Its whole requirement is that some CPU still takes an interrupt".
+`kernel/src/hardlockup/mod.rs`'s header repeats it: "`crate::deadline` is the
+bound on a *machine*, and its whole requirement is that some CPU still takes
+an interrupt: it is polled from the timer entry."
-`poll` says the narrower thing (`deadline.rs:185-186`): "Whether this
-machine's bound has passed; the timer interrupt entry's, in both rings, and
-nothing else's." Its call sites are exactly two, both in
-`kernel/src/arch/x86_64/idt/timer.rs`: the Ring 0 naked entry's `call {deadline}`
-(`:71`, `deadline = sym crate::deadline::poll` at `:79`, placed there because
-"a CPU spinning on a ticket still takes this interrupt", `:61-63`) and
-`timer_handler`, which opens at `:92`: its first statement is
-`crate::irq_census::irq_took!(Timer);` at `:93`, and `crate::deadline::poll()`
-at `:96` is the second. `rg -n 'deadline::poll' kernel/src` finds no third. The same module already states the true requirement once, in
-`armed`'s doc (`deadline.rs:86-87`): "Both bounds rest on some CPU taking a
-timer interrupt".
+The calls say the narrower thing. `poll` and `poll_in_kernel` are reached from
+the timer entries alone: x86-64's Ring 0 naked entry and `timer_handler`
+(`kernel/src/arch/x86_64/idt/timer.rs`), and AArch64's timer arm
+(`kernel/src/arch/aarch64/trap.rs`); `rg -n 'deadline::poll' kernel/src` finds
+no other. The same module already states the true requirement once, in
+`armed`'s doc: "Both bounds rest on some CPU taking a timer interrupt".
-So a CPU taking xHCI interrupts, IPIs, or the hard-lockup detector's own NMI
-sample every second (`hardlockup/mod.rs:9-14`) — each an interrupt the
+So a CPU taking device interrupts, IPIs, or the hard-lockup detector's own NMI
+sample every second (`hardlockup/mod.rs`'s header) — each an interrupt the
header's sentence counts — never reaches `poll`. The header claims coverage
of a machine on which some CPU takes *an* interrupt; the code covers a
machine on which some CPU takes a *timer* interrupt. A T14 boot on
@@ -37,8 +32,7 @@ timer ticks, and a bound that fired 132859 ms late. Whether that boot's 133 s
were a machine with no timer interrupt on any CPU is unmeasured; that such a
machine is outside what the code does and inside what the header says is not.
-**Exit condition**: the header and the call sites say the same thing —
-either `:11-13` and `hardlockup/mod.rs:4-5` say "timer interrupt" and the
-machine that takes interrupts but no timer tick is listed under "What it does
-not cover" (`:17-32`), or `poll` is reached from every interrupt entry and
-`:185-186` says so.
+**Exit condition**: the headers and the call sites say the same thing —
+either both headers say "timer interrupt" and the machine that takes
+interrupts but no timer tick is listed under `deadline.rs`'s "What it does not
+cover", or `poll` is reached from every interrupt entry and its doc says so.
diff --git a/issues/the-diary-computes-no-lateness-and-records-no-slow-system-call.md b/issues/the-diary-computes-no-lateness-and-records-no-slow-system-call.md
index 233bb0ef68a..1cc2444db8e 100644
--- a/issues/the-diary-computes-no-lateness-and-records-no-slow-system-call.md
+++ b/issues/the-diary-computes-no-lateness-and-records-no-slow-system-call.md
@@ -52,9 +52,7 @@ anything more is built on it.
kernel reads back from the trace with its number and its program. A
window's record names its opener by address, which
`issues/the-cpu-that-spawns-a-toybox-applet-reads-1-4-ms-of-preemption-off-on-the-t14.md`
- and
- `issues/the-supervisors-claim-of-a-pci-function-the-t14-lacks-holds-preemption-off-for-3-8-ms.md`
- need.
+ needs.
**Ruled** (owner, 2026-10-04), on when these steps start, **"Both in
parallel"**: "Start the trace diary's first steps now, beside latency step 1
diff --git a/issues/the-gcd-maps-held-mmio-descriptors-are-logged-nowhere.md b/issues/the-gcd-maps-held-mmio-descriptors-are-logged-nowhere.md
new file mode 100644
index 00000000000..80e162e366b
--- /dev/null
+++ b/issues/the-gcd-maps-held-mmio-descriptors-are-logged-nowhere.md
@@ -0,0 +1,30 @@
+---
+status: open
+kind: defect
+opened: 2026-09-14
+---
+
+# The GCD map's held MMIO descriptors are logged nowhere
+
+`loader/src/gcd.rs`'s `free_mmio` prints one line per free MMIO range it hands
+the kernel and a count of the map's descriptors, and no line for any other
+descriptor. So the map's held MMIO descriptors — firmware's *current*
+allocations, the reading that lets that file's module header be checked against
+a real machine — reach no console and no file. Eighteen of the ThinkPad T14's 75
+descriptors are MMIO and five are logged; eleven of q35's 34 are MMIO and two
+are logged.
+
+What kept them out is gone. The loader's whole map, moved into `loader.log` on
+`bar-placement` at `ffd3f9c2`, once cost the panel its kernel log, because
+`/system/bin/console` seeded the screen from every `*.log` file in `/log`. It
+now seeds from the boot `logkeeper` hands it over the log port
+(`seed`, `system/console/src/main.rs`), so by reading, a line the loader writes
+to its own file alone (`loaderlog::line`) costs no panel row; no boot has
+checked that.
+
+What pays for it is `toyos-metal`'s readback of `loader.log`
+(`tests/metal/src/lib.rs`): a machine whose aperture is in neither channel
+cannot have that premise checked against it afterwards.
+
+**Exit condition.** The loader writes one line per GCD descriptor to its file
+alone, and a T14 readback of `loader.log` carries the whole map.
diff --git a/issues/the-global-pipe-lock-spans-a-user-copy.md b/issues/the-global-pipe-lock-spans-a-user-copy.md
index 228ae2cb30a..9896a4e8660 100644
--- a/issues/the-global-pipe-lock-spans-a-user-copy.md
+++ b/issues/the-global-pipe-lock-spans-a-user-copy.md
@@ -6,51 +6,107 @@ opened: 2026-08-28
# `pipe::try_read`/`write` hold the one global `PIPES` spinlock across a 2 MiB user copy, and a pipe's first write adds a bitmap scan and a 2 MiB zeroing under it
-Every pipe and every IPC `Connection` in the machine lives behind one static: `static PIPES: Lock