Repository navigation
Take endpoint-libs 3.2 in blitz-control-protocol (0.5.3) - #24
Merged
Merged
Conversation
added 16 commits
September 23, 2026 11:59
Inspect, Act, Relaunch and Quit cannot name a second URL. Navigate carries one; the document core refuses it because a fetch belongs to the embedder. 0.5.2 so chuzz can path-dep this checkout until it publishes.
… wire keywords, summary, preview, structure and content, each reporting the nodes and bytes it dropped as a ratio so a caller can tell whether widening helps. Summary is extractive: sentences the page contains, checkable against it. The keyword ranker carries keyword_margin, terms against an unrelated page of the same kind, because a raw score without a null is decoration. Response is level-shaped (Condensation), so a caller asking for keywords is not handed the page's text; the cache holds every level per settle key so widening is a second cheap request rather than a second settle. Keyed on URL and settle revision, never the fetch. DocumentControl answers it too, so the document core and the headless host agree.
3.2 removed `framed_json` along with the tokio flavour of the transport. `framed_json_neutral` replaces it and takes a `futures_io` stream, which every caller in this workspace does not have: they hold tokio `UnixStream`s and a tokio duplex in the round-trip test. So the `compat` bridge lives in one place. `framed_json` stays as this crate's own function with the same name and the same shape, wrapping `framed_json_neutral` over `tokio_util::compat`, and `client.rs` and `ps-qa` are untouched by the change. The codec on the wire is unchanged. It is gated on `server` or `client`, the two features that put tokio in the graph, and `tokio-util` joins both. A build that only reads the semantic tree has no socket to frame, and karen takes this crate with default features off precisely to get that build; leaving the shim ungated would have put tokio back into it. The resolved endpoint-libs also moves 3.0.0 to 3.2.1. The requirement was already `^3` and needed no edit: the lock was simply stale, which is what made this look like a version pin problem rather than a removed function.
Missed in the previous pass: `server.rs` imported `framed_json` from endpoint-libs directly, which 3.2 removed along with the tokio flavour of the transport. It uses the shim in `lib.rs` now, the same one `client.rs` and ps-qa already use, so the compat bridge stays in one place. 0.5.3, because a consumer resolving this from the registry rather than by path needs a release to pick the fix up.
Groundwork for taking tokio out of the control socket. The two tokio channels in this crate's public API are not carrying queues: a watch retains one revision and a oneshot carries one answer, so what was being used in both cases was a retained slot and a wake. nagoya ships both halves, an RwLock and a Notify, and deliberately ships neither channel. Latest replaces the watch. A reader that falls behind skips the revisions it missed rather than queueing them, which is the property the event stream wanted: inspection stays bounded when a client stalls or an animation presents faster than the client consumes. Once replaces the oneshot, including the part that matters most: a sender going away calls abandon, so a caller waiting on an answer that will never come gets None instead of parking. A control request whose runtime went away has to fail, not hang the client. Both register the waiter with enable before reading the slot, which is the order nagoya documents on Notified::enable: notify_waiters stores no permit, so a waiter that reads first can lose a transition landing between the read and the first poll. Once keeps its value and its finality flag under one lock rather than two, because two cannot be read together: a receiver could find the slot empty, lose a race to send, then read a finality flag that is now true and report None while the value sits there. Four tests, one per property, including the race enable exists for.
The socket path was world-reachable between bind and chmod, and it was not a theoretical window. A Unix socket is created by bind already listening, so a mode applied afterwards is always late, and the directory under /tmp had whatever create_dir_all gives. The access control moves to the directory, set to 0700 before anything inside it exists. Ordering stops mattering: a process that cannot traverse the directory cannot reach the socket whatever the socket's own mode says. The socket keeps 0600 too, because defence resting on one check is defence resting on nobody ever relaxing the directory. The transport is nagoya's. TcpListener::bind takes an Addr and Addr::Path is a Unix socket, which is why there is no UnixListener in nagoya to look for: one socket type, the address decides the family. Binding moved onto the thread that polls it, since nagoya cannot adopt a raw fd and a socket only makes progress while its own reactor is polled. Connections are held in a FuturesUnordered rather than spawned. nagoya's TaskSet never removes a finished task's entry, so a set fed by unbounded connection churn grows one entry per connection ever accepted, and a QA harness reconnecting in a loop is exactly that churn. The watch and the oneshot in the public API are gone. ControlBridge returns an Arc<Once<DebugResponse>>; start_with_events takes an Arc<Latest<DebugEvent>>. Latest carries a revision and each connection holds its own cursor, which is what watch tracks internally: two readers sit at different places in the stream and one catching up must not move the other. That is also what preserves the rule the old borrow_and_update kept, that an action is not answered with a paint from before it. Shutdown is a Flag rather than a oneshot, because Drop is not async and cannot move a sender out to fire it. Tests still use tokio and are converted next.
The server path is nagoya end to end and the crate's server feature no longer reaches tokio at all: cargo tree -e normal -i tokio prints nothing for it. Binding moved back into start_inner, which is where it belongs. Doing it on the server thread was a race the tests caught at once: start returned with a path that nothing was listening on yet, so a caller that connected immediately got ENOENT. Listener::bind needs no reactor handle, so the socket is listening before start returns and the thread only registers it, which is the part that genuinely has to happen where the polling does. The seven socket tests are the reason to trust any of this, so they were converted rather than dropped: each drives a real server over a real socket. A test client needs a reactor of its own, because a nagoya socket only makes progress while the reactor it was created on is polled, and the server is polling its own on another thread. One shared Reactor::start covers all of them. Once gains a synchronous fill. The bridge closure is not async, so a host that already holds the answer has to reply without awaiting; contention is only against a receiver taking the value, so try_write either succeeds at once or means the answer was already delivered.
The client speaks to a nagoya socket now, like the server: Addr::path for the address, timeout and sleep from nagoya, and std Instant for deadline arithmetic, which never needed a runtime clock in the first place. The inspector connection gets a process-wide reactor rather than one per connection. A library does not own the caller's thread and cannot assume one is polling anything, and a thread per connection would be a thread per command in a QA sweep. With both halves converted, neither server nor client pulls tokio: cargo tree -e normal -i tokio prints nothing for either feature. tokio and tokio-util move behind tokio-framing, which exists only for the framed_json shim that ps-qa and chuzz still need for their own tokio streams. Depending on this crate no longer puts tokio in a graph that had got rid of it. The connect-retry test is kept and converted, because the race it covers is real: a descriptor can be visible before its socket is bound, and the client has to retry rather than fail. The duplex framing test stays on tokio behind the same feature, since the pipe it needs has no nagoya equivalent and the codec it checks is the same one either way.
framed_json was a bridge for consumers holding tokio streams, and after the server and the client both moved there were none left in this workspace. ps-qa's silent-host fixture was the last, and it binds a control socket, so it binds the same nagoya one the real server does rather than keeping a tokio listener alive to be bridged. The neutral framing and NagoyaStream are re-exported here instead. A consumer that serves or fakes this protocol names both halves of a connection through this crate rather than depending on endpoint-libs and nagoya to do it. The duplex round-trip test goes with the shim. It existed to exercise the tokio bridge, and the codec it checked is covered by the six socket tests that drive a real server over a real socket. tokio and tokio-util are gone from the manifest entirely, dependencies and dev dependencies alike. The only mentions left in the crate are three comments explaining what is no longer there.
Latest gains set_now, the synchronous twin of set. A paint lands in a window event handler, which is not async and has no runtime to await on, so the only way to record one was to have an async setter in a sync callback. try_write either succeeds at once or means a reader holds the slot for an instant, and dropping that revision is correct rather than unfortunate: this keeps only the newest value anyway, so a caller that loses the race is overwritten by the next paint a frame later.
The server thread returned silently when its reactor or the listener registration failed, so an app launched with --blitz-control advertised nothing and gave no reason. Both failures now go to stderr.
The inspector connection is a nagoya socket, and ps-qa awaited it from `#[tokio::main]`. Two runtimes: the request was written and the reply's readiness was never seen by the executor polling it. Its 43 `tokio::time` sleeps and deadlines had the same problem, since nagoya drives no tokio timer. main runs `nagoya::block_on`, sleeps are `nagoya::sleep`, deadlines are plain `std::time::Instant` arithmetic, and the one test that joined the silent host with the client does it with `futures::join!` under `nagoya::block_on`. tokio is no longer a dependency of ps-qa.
`Once::fill` gave up when `try_write` failed, on the reasoning that contention meant the answer was already delivered. It does not: `recv` holds the write lock while it looks at an empty slot, so a reply filled in that window was discarded and the receiver waited for one that never came, the same hang as a reply that was never sent. The receiver holds the lock only to read two fields, so fill now retries until it gets it.
The test module named MessageStream and TransportStream directly and then imported `super::*`, which re-exports both. Clippy with -D warnings rejects the duplicates, which failed the Linux protocol job.
The transport is nagoya end to end now; the feature comment still named tokio.
ps-qa now runs on nagoya and no longer depends on tokio.
pathscale
force-pushed
the
feat/qa-inventory-root
branch
from
September 23, 2026 04:59
3597889 to
d66cfb6
Compare
nagoya, ps-blitz, ps-anyrender and blitz-control-protocol were required at a patch floor. With no lockfile the newest release resolves regardless, so the floor pinned nothing useful and only had to be chased on every release.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
3.2 removed
transport::framed_jsonalong with the tokio flavour of the transport.framed_json_neutralreplaces it but takes afutures_iostream, and every caller in this workspace holds a tokio one.So
framed_jsonstays as this crate's own function, same name and shape, wrappingframed_json_neutralovertokio_util::compat.client.rs,server.rsandps-qaare untouched by the change and the codec on the wire is unchanged.Gated on
serverorclient, the two features that put tokio in the graph, withtokio-utiljoining both. karen takes this crate with default features off precisely to get the tokio-free build; leaving the shim ungated would have put tokio back into it.0.5.3 so a consumer resolving from the registry rather than by path can pick the fix up.
The resolved endpoint-libs moves 3.0.0 to 3.2.1. The requirement was already
^3and needed no edit: the lock was stale, which is what made this look like a pin problem rather than a removed function.Verified:
cargo check --all-targetsandcargo clippyclean.