/.
The settings document now says where it came from
The engine traps withCan't initialize the TaskScheduler before flags have been loaded when it is handed an empty client-settings document. Cordial printed
nothing about that document at all, so the crash looked like a race and was
diagnosed as one — wrongly, for one of the two reporters.
The default launch path now prints the size and the source:
fetched, stale cache, fetch failed, or nothing: with the reason. An unreadable path given with
--client-settings now says so instead of silently falling through to the
network, which previously looked exactly like a healthy launch with nothing
behind it.
Measured: a run forced down the empty-document path reproduces the reported
crash exactly, printing client settings: 0 bytes before
nativeInitClientSettings -> 1; three control runs in the same session each
printed 1284288 bytes (cache) and did not crash.
The settings fetch had no timeout at all
It was a bareureq::get(URL).call() with no connect and no read deadline, so a
CDN that accepted a connection and then said nothing would hang the launch
indefinitely. It now uses the same timeouts as the updater: 10 seconds to
connect, 20 seconds in total.
Focused text was drawn too big
The editor drew text on a focused TextBox noticeably larger than the engine drew it unfocused, so a box visibly jumped as you clicked into it. Roblox’s font mapping carries afromRbxFontRatio per font id, and the editor ignored it.
Measured on font id 46, Builder Sans, ratio 0.7936507937: the capital “S” ink
height was 11px as the engine drew it, 15px in the editor before, and 12px
after. Thirteen of thirteen boxes checked.
Two font ids can share one file with different ratios, so the table stamps each
id’s own row rather than the file’s.
”/” opens chat on one press
Pressing/ needed two presses to open the chat box. The guard that stops a
focused TextBox eating game input was suppressing the key’s release as well:
by the time the release arrived the chat box had taken focus, so the engine went
on believing / was still held.
Releases are now paired to their presses — a release whose press was forwarded
is forwarded too. A press suppressed because a box already had focus is never
recorded, so its release is still suppressed, which is what keeps typing /
into an open chat box from reopening it.
Not yet confirmed against a live client. The pairing logic has unit tests
and the reasoning is written out at the change, but nobody has driven a real
/ at a real chat box and watched it. If one press still does not open chat,
or if a stray / now appears in the box, that is this change.
A sandboxed plugin dies with the client
A plugin’s sandbox outlived the client that started it. Killing it now kills the whole process group rather than one pid, and the sandbox is started with--unshare-all, --die-with-parent and --new-session.
Measured under an injected panic: seven orphaned sandboxes before, none after.
The signal case is not covered by that measurement, and is measured in
0.12.1 — see those notes. Drop does not run when the client dies by signal.
Discord Presence uses Cordial’s real application id
It was using a placeholder, so the presence that appeared was not Cordial’s. There is also now a way to change it.X11 pointer locking survived the move to Wayland
Pointer locking on X11 was reconciled against current main rather than lost, and X11 input now comes from XInput2 with the pointer warp as the fallback. ADR-028 records what the acceleration row does when XI2 is not available.The update path
Eleven separate faults, starting with the one that ate installs. Both archives now swap as a set or the install is left exactly as it was, rather than being replaced one file at a time.What is still broken
The startup freeze, when signed in. Not fixed, and now better characterised: two new specimens this cycle, one arriving after the home page rather than before it, and one where the settings menu lost a row immediately before freezing. Reopening usually works. A black canvas inside an experience. Joining works; what you see once there may not. The second half of the CachyOS crash. Two people hit the same error string for different reasons. The reporter whose settings document was rejected is now diagnosable. The reporter whose document was accepted, whose flags carry real values and who crashes later, is not fixed and not yet explained. The web-view dialog cursor is unverified, and the code says so. Whether the cursor reaches a verification or purchase dialog — and whether clicks leak through to the game underneath — has still never been observed live. One report says purchase popups now work; the code that would make them work carries anINFERRED comment saying it has not been run against a live dialog. Both remain
true.
Empty documents still crash. Deliberately. With no flags loaded the engine’s
assertion is telling the truth, and starting anyway would be a stub that lies —
failing later somewhere with no relationship to the cause. What changed is that
you can now see why in one line.
Nothing that was proposed and measured away
A TaskScheduler gate. The first diagnosis of the CachyOS crash was a startup race, and the fix was to make the client wait for flags before bringing up the TaskScheduler. For the reporter whose document was rejected there is nothing to wait for, and the gate — whose value is captured once and never re-read — would have been permanently shut, turning a crash into a hang. Dropped. Flushing stdout on the crash path. The theory was that a shorter crash transcript meant Rust discarded unflushed output. Rust’sio::Stdout is a
LineWriter unconditionally and flushes on every newline regardless of the
destination; 200,000 lines followed by an external SIGKILL lost none of them,
and the same crash captured to a file and through a pty came out identical. The
transcripts differ because the two reporters crash at different points. No flush
code was added.