From b3063c68266727d583da467f09b9c4571a70b959 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 10 Nov 2026 20:03:28 -0800 Subject: [PATCH 1/3] docs(quest): record the maintainer's 2026-11-11 unblock decisions Co-Authored-By: Claude Opus 5.5 --- quest/m1/ffi-refusals/generics.md | 20 ++++++++----------- quest/m1/ffi-refusals/moq-pin.md | 14 +++++--------- quest/m1/ffi-refusals/small-shapes.md | 15 ++++++-------- quest/m1/ffi-refusals/std-types.md | 28 ++++++++++----------------- quest/m4/fork.md | 4 ++++ 5 files changed, 33 insertions(+), 48 deletions(-) diff --git a/quest/m1/ffi-refusals/generics.md b/quest/m1/ffi-refusals/generics.md index c9f44b21d..f94ad80d2 100644 --- a/quest/m1/ffi-refusals/generics.md +++ b/quest/m1/ffi-refusals/generics.md @@ -26,15 +26,11 @@ and listed in the census. moq-mux's `codec::annexb::build_prefix` exports once m re-exporting `bytes` whole, as [moq-pin](/quest/m1/ffi-refusals/moq-pin.md) has it, since the re-export makes `Bytes` a handle. -## Waiting - -- Maintainer: how a future or a kept closure crosses from C. Options: (a) the exporter gains - a foreign future, a poll function, its data, and `free`, the inverse of the futures it - returns, and a kept closure, a function, its data, and `free`, each wrapped in every - language's native future and function, so moq changes nothing; (b) moq's signatures - change, such as the stand-in re-exporting only kio's `Instant` from `clock`, and kio's and - moq-tokio's runtime helpers leaving what the facade reaches. Recommendation: (a), since - each has a C shape and AGENTS.md counts a source change a consumer needs as a gap; `repair` - then takes a foreign `Read + Seek`, while `panic::message` and `tls::Serve::resolver`, which - name what C can never hold, go to moq to wrap or drop, as the epic decided for third-party - types. +Decided 2026-11-11 (maintainer, /quest-unblock): ✅ (a) the exporter gains a foreign future (a +poll function, its data, and `free`, the inverse of the futures it returns) and a kept closure +(a function, its data, and `free`), each wrapped in every language's native future and +function, so moq changes nothing (rejected: (b) moq's signatures change). `repair` then takes +a foreign `Read + Seek`; `panic::message` and `tls::Serve::resolver` go to moq to wrap or +drop. The maintainer dislikes callbacks and closures in an API, so a moq quest for a +closure-free API is planned separately only if a simpler one exists; it does not gate this +quest. diff --git a/quest/m1/ffi-refusals/moq-pin.md b/quest/m1/ffi-refusals/moq-pin.md index 6e96ee00c..5c4a24c40 100644 --- a/quest/m1/ffi-refusals/moq-pin.md +++ b/quest/m1/ffi-refusals/moq-pin.md @@ -55,6 +55,11 @@ Decided with the maintainer on 2026-11-09: bump to moq's current main now, and n Goal to the two moq-owned types. Rejected: wait for moq to fix the rest first. Not landed: moq's `main` fails `just ts moq` and `just ffi moq-cpp`, so the bump waits on Waiting. +Decided 2026-11-11 (maintainer, /quest-unblock): ✅ (a) rust2 exports a type the crate root does +not reach by its defining path, such as `kio::Pending` and `lite::version::Version`; a type +whose defining path is itself private stays refused (rejected: (b) moq makes both reachable). +The exporter change lands before the bump. + ## Waiting - moq-dev/moq: `rind` builds for wasm32. moq-net depends on `rind`, whose md5 inline asm has @@ -62,12 +67,3 @@ moq's `main` fails `just ts moq` and `just ffi moq-cpp`, so the bump waits on Wa - moq-dev/moq: moq-tokio builds without tripping `recursion_depth_exceeding_limit` (an overflow proving `Send` on `Instrumented<...>`), which `just ffi moq-cpp ` denies. Done when it passes at moq's `main`. -- Maintainer: decide how `kio::Pending`, named by `dial_tcp` and `dial_track`, and - `lite::version::Version` stop refusing as "type not reachable from the crate root", which - `just ffi census ` lists at moq's `main`. Options: (a) rust2 exports a type the - crate root does not reach by its defining path, recommended, since it asks moq for no source - change, as the maintainer's 2026-11-09 decision does; a type whose defining path is itself - private stays refused. (b) moq-dev/moq makes both reachable from moq-net's root, by name or - by wrapping; no moq quest owns this yet. Whichever is chosen, its change lands before the - bump: the exporter change under (a), or moq's fix under (b). Done when the census at moq's - `main` lists neither type. diff --git a/quest/m1/ffi-refusals/small-shapes.md b/quest/m1/ffi-refusals/small-shapes.md index 9394be8ae..f434f7830 100644 --- a/quest/m1/ffi-refusals/small-shapes.md +++ b/quest/m1/ffi-refusals/small-shapes.md @@ -16,12 +16,9 @@ change its signature. [moq-dev/rust2#675](https://github.com/moq-dev/rust2/pull/675); `Result` and `!` off a return left the census with the narrowed stand-in. -## Waiting - -- Maintainer: how `[T; N]` maps. Options: (a) a returned `[u8; N]` is bytes, as `Vec` - is, and a parameter `[u8; N]` is bytes whose length the glue checks, raising the - language's error on a mismatch; (b) a C struct `_array__` holding the - array, with each language's fixed-size form above it; (c) leave arrays refused and - keep this quest open until a consumer needs one. Recommended: (a), which reuses the - bytes every binding already has and clears both with no new type; any other element - type stays refused until one is needed. +Decided 2026-11-11 (maintainer, /quest-unblock): ✅ (b) `[T; N]` crosses as a C struct +`_array__` holding the array by value, with each language's fixed-size form above +it, such as Swift's `InlineArray` and a generated Kotlin type that checks its size: a proper +new type, with no allocation, that C compiles out (rejected: (a) bytes with a length check; +(c) leaving arrays refused). Element types beyond `u8` follow the same shape when one is +needed. diff --git a/quest/m1/ffi-refusals/std-types.md b/quest/m1/ffi-refusals/std-types.md index 2cf2d31dc..f8cc7858d 100644 --- a/quest/m1/ffi-refusals/std-types.md +++ b/quest/m1/ffi-refusals/std-types.md @@ -17,25 +17,17 @@ The rest of the original count lies in moq-tokio outside its session side (`NonZ ## Plan -Each item below waits on a maintainer decision; once decided, each is a change in +Each decision below is a change in `ffi/src/collect.rs` and `ffi/src/glue.rs`, with the api fixture and its seven language tests covering it. -## Waiting +Decided 2026-11-11 (maintainer, /quest-unblock): -- Maintainer: `Filter` collects at the call or stays refused. Collecting runs the predicate - on every item at the call, which a Rust caller that stops early would not run on the - rest; `ffi/fixtures/reject`'s `words` refuses a `Split` by a closure for that reason, so - collecting reverses it for both. The predicate is moq's own, pure, and a C caller can - only take every item, as a Rust caller that collects does. Recommended: collect, for - items whose drop runs nothing, so a refused item's drop never moves to the call. -- Maintainer: `serde_json`'s map iterators (`Iter`, `IntoIter`, `Keys`, `Values`) collect - as core's do, or stay refused. The exporter already trusts serde_json for `Json`, and - they only walk the map. Recommended: collect them. -- Maintainer: `std::time::Instant` exports as an opaque object, compared and copied by its - traits with no methods of its own, or as nothing. It has no portable form C could read, - and `into_std` and `from_std` only move it between kio and Rust. Recommended: the opaque - object, adding no API std lacks. -- Maintainer: `tracing::Level` exports as a plain enum of its five levels, with - `LevelFilter` as an `Option` whose `None` is `OFF`, or as its text, which a parse - could fail on. Recommended: the enum, which no input can make invalid. +- ✅ `Filter` collects at the call, for items whose drop runs nothing, reversing the `words` + fixture's `Split` refusal for the same case (rejected: staying refused). +- ✅ `serde_json`'s map iterators (`Iter`, `IntoIter`, `Keys`, `Values`) collect as core's do + (rejected: staying refused). +- ✅ `std::time::Instant` exports as an opaque object, compared and copied by its traits with + no methods of its own (rejected: exporting nothing). +- ✅ `tracing::Level` exports as a plain enum of its five levels, with `LevelFilter` as an + `Option` whose `None` is `OFF` (rejected: its text). diff --git a/quest/m4/fork.md b/quest/m4/fork.md index 16cc1c470..c9a628aa8 100644 --- a/quest/m4/fork.md +++ b/quest/m4/fork.md @@ -48,6 +48,10 @@ Each names what the fork changes, the measured gain on moq, and its source. Public API: none until the fork has a consumer. +Decided 2026-11-11 (maintainer, /quest-unblock): the reasons above are not yet enough to pay +for a fork; this ledger stays, and its wait reopens as later reports add evidence (rejected: +fork now; deleting the ledger). + ## Waiting - Maintainer: decide that the reasons to fork above add up to enough to pay for From 0a0c7bcde4a664753715ca63ff043d366a55d76d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 10 Nov 2026 20:05:54 -0800 Subject: [PATCH 2/3] docs(quest): fix review findings and record the 2026-11-11 moq research moq-pin: Goal no longer points at the removed wait; this quest owns the exporter change; the two moq waits and the stale ffi.toml entries carry dated evidence at moq d528b675. generics: four items leave without (a). small-shapes resizes to S for option (b). fork: the wait waits on new evidence instead of re-asking the settled question. Co-Authored-By: Claude Opus 5.5 --- quest/m1/ffi-refusals/generics.md | 17 +++++++++++++++-- quest/m1/ffi-refusals/moq-pin.md | 15 ++++++++++++--- quest/m1/ffi-refusals/small-shapes.md | 2 +- quest/m1/ffi-refusals/std-types.md | 5 ++--- quest/m4/fork.md | 7 ++++--- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/quest/m1/ffi-refusals/generics.md b/quest/m1/ffi-refusals/generics.md index f94ad80d2..21cd74016 100644 --- a/quest/m1/ffi-refusals/generics.md +++ b/quest/m1/ffi-refusals/generics.md @@ -32,5 +32,18 @@ poll function, its data, and `free`, the inverse of the futures it returns) and function, so moq changes nothing (rejected: (b) moq's signatures change). `repair` then takes a foreign `Read + Seek`; `panic::message` and `tls::Serve::resolver` go to moq to wrap or drop. The maintainer dislikes callbacks and closures in an API, so a moq quest for a -closure-free API is planned separately only if a simpler one exists; it does not gate this -quest. +closure-free API is planned separately only if research finds a simpler one; it does not gate +this quest. + +Found on 2026-11-11 at moq's `main` `d528b675`, four of the eleven leave without (a): + +- moq-tokio's `server::Request::within` leaves through a planned moq quest that replaces it + with `Request::poll_expired` and `expired()`. +- `kio::wait` stops being reachable once the [pin bump](/quest/m1/ffi-refusals/moq-pin.md) + drops `pub use kio;`. +- moq-tokio's `panic::isolate` leaves when the stand-in in `ffi/justfile` stops re-exporting + `moq_tokio::panic`, from which moq-ffi ships nothing; this quest makes that change, and + `panic::message` leaves with it. + +kio's other five, `Waiter::poll_future`, `clock::timeout`, `clock::timeout_at`, +`clock::drive`, and `Waiter::batch`, still need (a), as `repair` does. diff --git a/quest/m1/ffi-refusals/moq-pin.md b/quest/m1/ffi-refusals/moq-pin.md index 5c4a24c40..f026d0e6d 100644 --- a/quest/m1/ffi-refusals/moq-pin.md +++ b/quest/m1/ffi-refusals/moq-pin.md @@ -5,7 +5,7 @@ The moq pin moves to moq's current `main`, whose crates re-export no whole crate, moq's own included. `just ffi census` there lists no whole-crate re-export refusal. Its "type not reachable from the crate root" refusals are `kio::Pending` and `lite::version::Version`, -which Waiting covers, and moq-quic's stats and varint types under moq-tokio's `quic::driver`, +which the exporter change below exports, and moq-quic's stats and varint types under moq-tokio's `quic::driver`, which [moq-dev/rust2#683](https://github.com/moq-dev/rust2/pull/683) drops. At `ffcb7cd9` there were 20 such refusals (18 Guard and Side, and `Version`), and 9 at moq's head on 2026-11-09, measured in [#676](https://github.com/moq-dev/rust2/pull/676). @@ -58,12 +58,21 @@ moq's `main` fails `just ts moq` and `just ffi moq-cpp`, so the bump waits on Wa Decided 2026-11-11 (maintainer, /quest-unblock): ✅ (a) rust2 exports a type the crate root does not reach by its defining path, such as `kio::Pending` and `lite::version::Version`; a type whose defining path is itself private stays refused (rejected: (b) moq makes both reachable). -The exporter change lands before the bump. +This quest makes the exporter change, in `ffi/src/collect.rs`, before the bump. + +Measured on 2026-11-11 at moq's `main` `d528b675`: the bump also has to fix 37 stale +`ffi/moq/ffi.toml` entries, such as excludes naming `bytes` and `kio`, which no longer +exist, and the new refusals the census lists there. ## Waiting - moq-dev/moq: `rind` builds for wasm32. moq-net depends on `rind`, whose md5 inline asm has no wasm32 arm, so `just ts moq ` fails at moq's `main`. Done when it passes there. + Still failing on 2026-11-11 at moq `d528b675`: `rind` 0.3.1, and 0.3.2 too, has an + unconditional `asm!` in `src/low/generic/md5.rs:22`, so the wasm32 build fails with E0658. + The fix belongs in `rind` itself, then a bump in moq. - moq-dev/moq: moq-tokio builds without tripping `recursion_depth_exceeding_limit` (an overflow proving `Send` on `Instrumented<...>`), which `just ffi moq-cpp ` denies. - Done when it passes at moq's `main`. + Done when it passes at moq's `main`. Still failing on 2026-11-11 at moq `d528b675`, at + `rs/moq-tokio/src/client.rs:722`, on the `Instrumented<...>` from + `tokio::spawn(panic::isolate(time::run(driver)).instrument(..))`. diff --git a/quest/m1/ffi-refusals/small-shapes.md b/quest/m1/ffi-refusals/small-shapes.md index f434f7830..9c16d3dde 100644 --- a/quest/m1/ffi-refusals/small-shapes.md +++ b/quest/m1/ffi-refusals/small-shapes.md @@ -1,4 +1,4 @@ -# [XS] Arrays export +# [S] Arrays export ## Goal diff --git a/quest/m1/ffi-refusals/std-types.md b/quest/m1/ffi-refusals/std-types.md index f8cc7858d..0e9ccfd50 100644 --- a/quest/m1/ffi-refusals/std-types.md +++ b/quest/m1/ffi-refusals/std-types.md @@ -17,9 +17,8 @@ The rest of the original count lies in moq-tokio outside its session side (`NonZ ## Plan -Each decision below is a change in -`ffi/src/collect.rs` and `ffi/src/glue.rs`, with the api fixture and its seven language -tests covering it. +Each decision below is a change in `ffi/src/collect.rs` and `ffi/src/glue.rs`, with the api +fixture and its seven language tests covering it. Decided 2026-11-11 (maintainer, /quest-unblock): diff --git a/quest/m4/fork.md b/quest/m4/fork.md index c9a628aa8..1bfdbeeac 100644 --- a/quest/m4/fork.md +++ b/quest/m4/fork.md @@ -49,11 +49,12 @@ Each names what the fork changes, the measured gain on moq, and its source. Public API: none until the fork has a consumer. Decided 2026-11-11 (maintainer, /quest-unblock): the reasons above are not yet enough to pay -for a fork; this ledger stays, and its wait reopens as later reports add evidence (rejected: -fork now; deleting the ledger). +for a fork; this ledger stays, and the maintainer decides again once a later report adds +evidence (rejected: fork now; deleting the ledger). ## Waiting -- Maintainer: decide that the reasons to fork above add up to enough to pay for +- Maintainer: once a report adds a reason or a measured gain to this ledger after the + 2026-11-11 decision, decide whether the reasons to fork add up to enough to pay for maintaining one. An agent can only gather the evidence; the cost of carrying a fork is the maintainer's to weigh. From cb2b278ea3b7ba31445caf1185d55fef0ac1252f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 10 Nov 2026 20:07:11 -0800 Subject: [PATCH 3/3] docs(quest): keep the Filter decision's reason for giving up laziness Co-Authored-By: Claude Opus 5.5 --- quest/m1/ffi-refusals/std-types.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/quest/m1/ffi-refusals/std-types.md b/quest/m1/ffi-refusals/std-types.md index 0e9ccfd50..42de930bd 100644 --- a/quest/m1/ffi-refusals/std-types.md +++ b/quest/m1/ffi-refusals/std-types.md @@ -23,7 +23,10 @@ fixture and its seven language tests covering it. Decided 2026-11-11 (maintainer, /quest-unblock): - ✅ `Filter` collects at the call, for items whose drop runs nothing, reversing the `words` - fixture's `Split` refusal for the same case (rejected: staying refused). + fixture's `Split` refusal for the same case (rejected: staying refused). Collecting runs + the predicate on every item at the call, which a Rust caller that stops early would not; + that is accepted because the predicate is moq's own and pure, and a C caller can only take + every item, as a Rust caller that collects does. - ✅ `serde_json`'s map iterators (`Iter`, `IntoIter`, `Keys`, `Values`) collect as core's do (rejected: staying refused). - ✅ `std::time::Instant` exports as an opaque object, compared and copied by its traits with