10 interesting stories served every morning and every evening.

Your ‘App’ Could Have Been a Webpage (so I fixed it for you…)

danq.me

Why is this an app”?

This sum­mer, the kids’ per­form­ing arts school are singing and danc­ing in a show at Disneyland. We’re all very ex­cited, but my ex­cite­ment, at least, was muted a lit­tle when I was told to in­stall the Travelbound” app in or­der to get ac­cess to the itin­er­ary, travel arrange­ments, and ac­com­mo­da­tion de­tails.

Fuck that noise. This should have been a web­page. Why do you want me to in­stall a(nother) shitty app just to tell me some­thing that could have been a (smaller, faster, more uni­ver­sally-ac­ces­si­ble) doc­u­ment?

There only seem to be two things that this app” does, that a web­page might not have, and they’re both anti-fea­tures:

It re­ports track­ing data as­so­ci­ated with your Google Account back to the de­vel­op­ers.

It shows you ad­ver­tise­ments (which they call inspirations”) for other trips or­gan­ised by the same agency.

Fuck. Everything. About. That.

A web­page would have been so much bet­ter. Unlike this app, a web­page can be…1

Copy-pastable

Printable

Saveable

Bookmarkable

Searchable

Usable on vir­tu­ally any de­vice

(Potentially) more-ac­ces­si­ble

I’m an­noyed enough… that I’m go­ing to fix” this app. Hold my beer.

Intercepting app traf­fic

It’s been a while since the last time I re­verse-en­gi­neered an Android app from its net­work traf­fic, so I had to brush-up on the best way. Here’s what I ended up do­ing.

Created a new vir­tual de­vice in Android Studio’s Virtual Device Manager.

Tested adb shell was work­ing and used rootAVD to root it: ./rootAVD.sh sys­tem-im­ages/​an­droid-33/​google_apis_­play­store/​x86_64/​ramdisk.img.2

Performed a cold boot, ran Magisk, and tweaked its set­tings to au­to­mat­i­cally grant su ac­cess to any app that asked.3

Ran HTTP Toolkit and told it to in­ter­cept AVD traf­fic. It in­stalled a (fake) VPN provider, rout­ing the phone’s traf­fic through the proxy.4

Installed the Travelbound app from the Play Store.

Configured HTTP Toolkit to proxy only the Travelbound app (more sig­nal, less noise).

With only a cou­ple of min­utes ex­per­i­men­ta­tion I dis­cov­ered that the app works by con­cate­nat­ing the user­name and pass­word5 and us­ing it in a URL of the form:

https://​trav­el­bound.api.va­moos.com/​api/​itin­er­aries/{​user­name}-{pass­word}

https://​trav­el­bound.api.va­moos.com/​api/​itin­er­aries/{​user­name}-{pass­word}

This re­turns a pile of JSON which, with a lit­tle in­ter­pre­ta­tion, can be seen to rep­re­sent all of the con­tent the app shows”. E.g., there’s:

an ar­ray con­tain­ing each leg of the itin­er­ary,

an ar­ray con­tain­ing all of the inspirations” ad­ver­tise­ments to show you,

a cross-ref­er­enced ar­ray con­tain­ing all of the files (images etc.) that are ref­er­enced by the other sec­tions, etc.

A lit­tle ex­per­i­men­ta­tion showed me that the S3 im­age URLs were be­ing de­liv­ered with mod­er­ately-short ex­pi­ra­tion times, so the JSON needs re-fetch­ing pe­ri­od­i­cally even if the con­tent has­n’t been changed.6

Turning it into some­thing bet­ter

Now I had every­thing I needed to make some­thing… bet­ter. I wrote a Ruby script that runs on a Cron sched­ule to pull the lat­est JSON and use it to build a HTML page.

I chose to have it com­pletely skip over the inspirations” (“overlayRows” in the data schema) and just list:

the items from the itin­er­ary and

all of the files not ref­er­enced by the in­spi­ra­tions nor itin­er­ary, (a lazy way to col­late the PDF down­load links).

Then I hosted the page, pro­tected by a pass­word: the same one my tour group were given in the first place. I in­cluded the raw JSON it used in <details> el­e­ments so it can be checked if e.g. there are bits of the schema I did­n’t see but that might ap­pear later.

Some peo­ple like an app”, and that’s… fine, I guess. But some apps could have been a web­page. And es­pe­cially where, like this one, the con­tent they de­liver is al­ready writ­ten in HTML and de­liv­ered over HTTP… they should be a web­page, right?

I can’t un­der­stand how we got to this place with app cul­ture”! Software com­pa­nies are happy to make their lives harder (and more ex­pen­sive: de­ploy­ing to the big app stores is­n’t free!), in or­der to de­liver HTML con­tent to fewer peo­ple and with fewer fea­tures7 than if they just pub­lished di­rectly to the Web in the first place!

There are (some) tasks for which an app” is ab­solutely the right choice of medium. Travelbound is not one of them.

But at least I (and the rest of our group, whom I’ve shared it with) now get the choice about how we ac­cess this con­tent. Either a 43MB app (ballooning to 124MB when it’s fin­ished down­load­ing ex­tra con­tent) with track­ing and ad­ver­tise­ments… or a 0.05MB web page (with an op­tional ex­tra 35MB of im­ages) that pro­vides more fea­tures and works on more de­vices. I know which one I’ll be us­ing!

Footnotes

1 And these are just the fea­tures that every­body can get be­hind. The web­page I ul­ti­mately ended up mak­ing to re­place the app also has some user-friendly/​de­vel­oper-hos­tile fea­tures, like the fact that it re­moves the track­ing code and does­n’t show ad­ver­tise­ments.

2 You need to root the de­vice in or­der to force ap­pli­ca­tions that use Certificate Pinning to trust your man-in-the-mid­dle proxy server. Without this, some ap­pli­ca­tions — in­clud­ing the one I wanted to re­verse-en­gi­neer — will recog­nise your self-signed TLS cer­tifi­cate as in­valid and refuse to com­mu­ni­cate.

3 Without chang­ing this set­ting in Magisk, I found that HTTP Toolkit would re­quest su ac­cess but not wait for the re­sponse, and go on to run in un­priv­i­leged mode be­fore I had a chance to grant it!

4 Owing to Android se­cu­rity con­sid­er­a­tions I needed to man­u­ally in­stall the root CA cer­tifi­cate it in­stalled for me, but the in­struc­tions just worked”.

5 The user­name and pass­word is shared by an en­tire tour group. I’m guess­ing they don’t have a plan for if some cre­den­tials get leaked? Or pos­si­bly they con­sider all of the data they hold to be low-sen­si­tiv­ity enough that it does­n’t mat­ter if it does… in which case I re­turn to my orig­i­nal point: why the hell was­n’t it just a web­page in the first place?

6 Or else the im­ages need caching lo­cally, which seems to be what the app does, in the bloat­i­est pos­si­ble way.

7 And, of­ten, with worse ac­ces­si­bil­ity. I’ve not au­dited the ac­ces­si­bil­ity of this app, but there are things about it that sug­gest that it’d be harder to use us­ing ac­ces­si­bil­ity tech­nolo­gies than my plain, sim­ple Web ver­sion.

Japan develops a method to recover up to 90% of lithium from used EV batteries and it could be a major breakthrough

tech.supercarblondie.com

In a ground-break­ing step for­ward, Scientists from Japan have de­vel­oped a new method to re­cover up to 90 per­cent of lithium from used EV bat­ter­ies — and it sud­denly feels like great news on Earth Day.

With elec­tric ve­hi­cles boom­ing world­wide, the pres­sure is mount­ing to find smarter ways to deal with old bat­tery waste.

This new tech­nique does­n’t just re­cy­cle ma­te­ri­als; it re­cov­ers most of them at an un­be­liev­able rate.

And if it de­liv­ers at scale, it could change how EV bat­ter­ies are made and reused for years to come.

A new method to re­cover up to 90% of lithium from used EV bat­ter­ies

This huge break­through in tech has come from a re­cy­cling fa­cil­ity in Japan, where en­gi­neers have man­aged to ex­tract around 90 per­cent of lithium from used bat­ter­ies.

That’s a huge leap com­pared to tra­di­tional meth­ods, which of­ten re­cover less than 50 per­cent of the ma­te­r­ial, es­pe­cially since it feels like a win to cel­e­brate this Earth Day.

At the heart of the process is a clever chem­i­cal tweak; in­stead of us­ing stan­dard sodium hy­drox­ide, the team swapped in re­cov­ered lithium hy­drox­ide dur­ing re­cy­cling, which is a white pow­der.

This helps con­vert bat­tery waste, known as black mass’, into high-pu­rity lithium that can be reused in new bat­ter­ies.

Even bet­ter, the process is­n’t just ef­fi­cient, it’s bet­ter for the en­vi­ron­ment too, be­cause re­searchers say it can cut car­bon emis­sions by around 40 per­cent com­pared to con­ven­tional re­cy­cling tech­niques.

It could be a ma­jor break­through for the fu­ture of EVs

This mat­ters be­cause lithium is one of the most crit­i­cal in­gre­di­ents in EV bat­ter­ies, and de­mand is sky­rock­et­ing, as well as min­ing be­ing ex­pen­sive, en­ergy-in­ten­sive, and of­ten geopo­lit­i­cally com­pli­cated.

By re­cov­er­ing lithium do­mes­ti­cally, Japan could re­duce its re­liance on im­ports and sta­bilise sup­ply chains.

In fact, the coun­try cur­rently im­ports al­most all of its bat­tery min­er­als, so re­cy­cling at this scale could be a game-changer.

Massive geopo­lit­i­cal shift. NHK World con­firms Japan has per­fected a rev­o­lu­tion­ary process to ex­tract high pu­rity lithium from dead bat­ter­ies with a stag­ger­ing 90 per­cent re­cov­ery rate. This bril­liant tech­no­log­i­cal leap guar­an­tees Japan’s ab­solute eco­nomic se­cu­rity. pic.twit­ter.com/​O7ENxL­HcNb— Furkan Gözükara (@FurkanGozukara) April 8, 2026

Massive geopo­lit­i­cal shift. NHK World con­firms Japan has per­fected a rev­o­lu­tion­ary process to ex­tract high pu­rity lithium from dead bat­ter­ies with a stag­ger­ing 90 per­cent re­cov­ery rate. This bril­liant tech­no­log­i­cal leap guar­an­tees Japan’s ab­solute eco­nomic se­cu­rity. pic.twit­ter.com/​O7ENxL­HcNb

There are still chal­lenges, though: only about 14 per­cent of used lithium-ion bat­ter­ies in Japan cur­rently make it into of­fi­cial re­cy­cling sys­tems, mean­ing col­lec­tion in­fra­struc­ture needs a se­ri­ous up­grade.

But with plans to make pro­duc­tion even more pow­er­ful by 2027 and ex­tract tens of thou­sands of tons of ma­te­ri­als an­nu­ally by 2035, this in­no­va­tion could be a big turn­ing point.

If adopted glob­ally, it might not just change lives in Japan; it could save the world.

Do not add Google Play Integrity integration · eu-digital-identity-wallet/av-doc-technical-specification · Discussion #19

github.com

Worth a caveat be­fore Yivi gets held up as the pri­vacy-clean bench­mark. Its NFC pass­port flow is­n’t de­pen­dency-free ei­ther.

From the is­suer source (privacybydesign/go-passport-issuer): en­rolling a pass­port sends the raw NFC data groups to a cen­tral is­suer server. DG1 (full MRZ) and DG2 (facial im­age) are manda­tory in parseP­a­ss­port­DGs, and the server parses out name, doc­u­ment num­ber, na­tion­al­ity, DOB and gen­der. Face match­ing then runs through Regula’s third-party API.

So the au­then­tic­ity guar­an­tee comes from send­ing your full MRZ and fa­cial im­age to a cen­tral server plus a third-party bio­met­ric ser­vice. Yivi says it’s tran­sient and the is­suer is self-hostable, fair enough, but that’s still a cen­tral-is­suer de­pen­dency, not no de­pen­dency.” It’s a dif­fer­ent trust con­cen­tra­tion than de­vice at­tes­ta­tion, not an ob­vi­ously smaller one.

Refs: back­end/​mod­els/​pass­port_­val­i­da­tion_re­quest.go, back­end/​doc­u­ment/​pass­port/​pass­port.go, and https://​yivi.app/​en/​blog/​pass­port-au­then­ti­ca­tion/

Announcing Bonsai 27B: The First 27B-Class Model to Run on a Phone

prismml.com

Today, we’re an­nounc­ing Bonsai 27B, based on Qwen3.6 27B, the new mul­ti­modal flag­ship of the Bonsai fam­ily and the first model of its ca­pa­bil­ity class to run on a phone.

Our ear­lier re­leases proved that mod­els with 1-bit and ternary weights could pro­duce com­mer­cially use­ful lan­guage mod­els. Bonsai 27B ex­tends that fron­tier to a new ca­pa­bil­ity tier: multi-step rea­son­ing, struc­tured tool calls, vi­sion tasks, and com­puter-use agen­tic loops that stay co­her­ent across many steps. Until to­day, de­ploy­ing that tier lo­cally has been im­prac­ti­cal for a con­crete rea­son: a 27B model oc­cu­pies roughly 54GB in 16-bit pre­ci­sion, and even a good 4-bit build, at 18GB, is too large for a phone and for most lap­tops.

Bonsai 27B changes that. It comes in two vari­ants:

Ternary Bonsai 27B uses ternary {−1, 0, +1} weights with FP16 group-wise scal­ing, giv­ing a true 1.71 ef­fec­tive bits per weight. At 5.9 GB, it is the qual­ity-ori­ented vari­ant: it runs on an every­day lap­top with the full rea­son­ing, tool-call­ing, and agen­tic ca­pa­bil­ity.

1-bit Bonsai 27B uses bi­nary {−1, +1} weights with the same group-wise scal­ing, giv­ing 1.125 ef­fec­tive bits per weight. At 3.9 GB, it is the foot­print-ori­ented vari­ant, which fits within the mem­ory bud­get of an iPhone 17 Pro, bring­ing a 27B-class model onto a phone for the first time.

As with every Bonsai re­lease, the low-bit rep­re­sen­ta­tion runs end to end across the lan­guage net­work, em­bed­dings, at­ten­tion, MLPs, and the LM head, with no higher-pre­ci­sion es­cape hatches. Both vari­ants are mul­ti­modal, with the vi­sion tower ship­ping in a com­pact 4-bit form so on-de­vice work­flows can see screen­shots, doc­u­ments, and cam­era in­put, not just text. Bonsai 27B car­ries a full 262K-token con­text, and sup­ports spec­u­la­tive-de­cod­ing, com­pound­ing the speed with loss­less draft-and-ver­ify ac­cel­er­a­tion. Everything is avail­able to­day un­der the Apache 2.0 License.

Retaining the in­tel­li­gence

Across a 15-benchmark suite span­ning knowl­edge, rea­son­ing, math, cod­ing, in­struc­tion fol­low­ing, tool call­ing, and vi­sion  (evaluated in think­ing mode, where the mod­el’s full rea­son­ing is ex­er­cised) Ternary Bonsai 27B re­tains 95% of the full-pre­ci­sion base­line, and 1-bit Bonsai 27B re­tains 90%.

‍Fig I: Benchmark scores of Bonsai 27B (thinking mode) against the full-pre­ci­sion base­line. Full per-bench­mark re­sults are in the whitepa­per.‍

Read the table by ca­pa­bil­ity and the story is sharper than the av­er­ages: math and cod­ing are nearly un­touched, tool call­ing stays within a few points of full pre­ci­sion - ex­actly the ca­pa­bil­i­ties that agen­tic work­loads de­pend on. For com­par­i­son, the most ag­gres­sive con­ven­tional low-bit build of the same base model scores sig­nif­i­cantly lower than 1-bit Bonsai 27B while oc­cu­py­ing 2.5x more mem­ory.

This is the same Pareto shift we demon­strated with our ear­lier lan­guage and im­age mod­els, now at 27B scale: 27B-class ca­pa­bil­ity at a foot­print smaller than a full-pre­ci­sion 2B model. By in­tel­li­gence den­sity — the mea­sure we in­tro­duced with 1-bit Bonsai 8B — 1-bit Bonsai 27B de­liv­ers 0.53 per GB: more than 10x the full-pre­ci­sion base­line, and roughly 2.7x the best low-bit al­ter­na­tive avail­able.

Why this is an im­por­tant par­a­digm shift

The most valu­able AI work­loads are shift­ing from sin­gle re­sponses to sus­tained work: as­sis­tants that op­er­ate real tools, work­flows that run un­at­tended be­fore re­turn­ing a re­sult, and re­search that syn­the­sizes dozens of doc­u­ments. This shift changes the shape of the work­load — an agent does­n’t make one model call, it makes hun­dreds, each one car­ry­ing con­text, pro­duc­ing struc­tured out­put, and feed­ing the next.

Cloud APIs will re­main the right choice for many prod­ucts. But for agen­tic work­loads, cloud-only ex­e­cu­tion im­poses struc­tural con­straints: every step is a re­mote re­quest, per-to­ken cost ac­cu­mu­lates with every it­er­a­tion, and every plan, tool call, and in­ter­me­di­ate re­sult crosses the net­work in­clud­ing the user’s pri­vate files, screen, and data.

Carousel I: End-to-end agen­tic work­flow with Hermes, pow­ered by our Ternary Bonsai 27B model on NVIDIA GeForce RTX 5090.

Local ex­e­cu­tion changes the equa­tion. When a model ca­pa­ble of sus­tained agen­tic work fits on the de­vice, the agent can live in­side the prod­uct: the mar­ginal cost of a hun­dred-step loop is zero, and the user’s data never leaves the ma­chine. Entire cat­e­gories open up — per­sis­tent on-de­vice agents, as­sis­tants that work of­fline, as­sis­tants that rea­son over pri­vate lo­cal data by con­struc­tion. What has been miss­ing is a model small enough to de­ploy this way and ca­pa­ble enough to trust with the work. Bonsai 27B is that model.

It also un­locks a new sys­tem ar­chi­tec­ture: hy­brid de­ploy­ments that route non-fron­tier and pri­vacy-sen­si­tive tasks to a ca­pa­ble lo­cal model and re­serve fron­tier cloud mod­els for the hard­est steps — col­laps­ing the cost-per-task of agen­tic sys­tems.

Bonsai 27B reaches up to 163 tok/​s in 1-bit and 134 tok/​s in Ternary on an NVIDIA GeForce RTX 5090. On an M5 Max, it reaches up to 87 tok/​s in 1-bit and 58 tok/​s in Ternary.

Fitting a phone is a stricter gate than stor­age num­bers sug­gest. A phone never ex­poses its full mem­ory to an app - a 12 GB iPhone of­fers about 6 GB for the model to use on-de­vice, and the model shares that bud­get with its KV cache and ac­ti­va­tions. No con­ven­tional build of a 27B model comes close to clear­ing it. At about 4 GB, 1-bit Bonsai 27B is the first to pass through with room to work.

That con­straint is why the fam­ily ships two de­lib­er­ate op­er­at­ing points, specif­i­cally keep­ing that in mind: ternary for lap­top-class qual­ity, 1-bit for phone-class foot­print.

Demo II: Multimodal agen­tic use-cases pow­ered by 1-Bit Bonsai 27B on an iPhone 17 Pro Max (Demo Mode: Cached & Prefilled Image Context)

The fron­tier keeps mov­ing

Every Bonsai re­lease has moved the in­tel­li­gence-per-gi­ga­byte fron­tier left, and Bonsai 27B moves it past a prac­ti­cal thresh­old: the full ca­pa­bil­ity set of a mod­ern model with think­ing, mul­ti­modal un­der­stand­ing, vi­sion, re­li­able tool use, now fits on the de­vices peo­ple al­ready own.

We be­lieve in­tel­li­gence den­sity will be one of the defin­ing axes of the next stage of AI progress. Raw ca­pa­bil­ity de­ter­mines what a model can do; den­sity de­ter­mines where it can do it. Every left­ward shift of the fron­tier ex­pands the set of de­vices, prod­ucts, and en­vi­ron­ments where ad­vanced AI can op­er­ate and changes the eco­nom­ics of every de­ploy­ment sur­face it touches, from phones to sin­gle-GPU serv­ing. The method­ol­ogy be­hind Bonsai is ar­chi­tec­ture-ag­nos­tic, and the fron­tier will keep mov­ing: larger mod­els and new ar­chi­tec­tures are al­ready in progress.

Early com­put­ers filled rooms; to­day they live in our pock­ets. Intelligence is mak­ing the same jour­ney, and Bonsai 27B is its largest step yet.Plat­form Coverage

Bonsai 27B runs na­tively on Apple de­vices (Mac, iPhone, iPad) via MLX and on NVIDIA GPUs via CUDA, through cus­tom low-bit ker­nels built for its hy­brid-at­ten­tion ar­chi­tec­ture. Model weights are avail­able to­day un­der the Apache 2.0 License. With this re­lease, we’re of­fer­ing a free, lim­ited-time de­vel­oper pre­view API so de­vel­op­ers can eas­ily try our model.

Full tech­ni­cal de­tails of our com­pres­sion, eval­u­a­tion, and bench­mark­ing processes are avail­able in our whitepa­per.

Join Us

PrismML emerged from a team of Caltech re­searchers and was founded with sup­port from Khosla Ventures, Cerberus, and Google, with con­tin­u­ing sup­port from Samsung. We’ve spent years tack­ling one of the field’s hard­est prob­lems: com­press­ing neural net­works with­out sac­ri­fic­ing their rea­son­ing abil­ity.

If you want to help build the next gen­er­a­tion of state-of-the-art AI, we’d love to hear from you. Check out our ca­reers page.

How to stop Claude from saying load-bearing

jola.dev

Absolutely rip­ping your hair out read­ing Claude re­fer­ring to every­thing as honest takes” and load-bearing seams”? You’re not the only one. But what if I tell you there’s a way to take this mas­sive source of frus­tra­tion and make it so ridicu­lous you can’t but laugh at it? Or just sim­ply fix Claude’s vo­cab­u­lary. I pre­sent to you, the MessageDisplay hook.

First you need a lit­tle script with some re­place­ments set up:

#!/usr/bin/env python3 im­port json, re, sys re­place­ments = { seam”: whatchamacallit”, you’re ab­solutely right”: I’m a com­plete clown”, honest take”: spicy doo­dad”, load-bearing”: cooked” } data = json.load(sys.stdin) text = data.get(“delta”) or ” for phrase, re­place­ment in re­place­ments.items(): pat­tern = r”\b” + re.es­cape(phrase) + r”\b” text = re.sub(pat­tern, re­place­ment, text, flags=re.IG­NORE­CASE) print(json.dumps({ hookSpecificOutput”: { hookEventName”: MessageDisplay”, displayContent”: text, } }))

#!/usr/bin/env python3

im­port json, re, sys

re­place­ments = {

seam”: whatchamacallit”,

you’re ab­solutely right”: I’m a com­plete clown”,

honest take”: spicy doo­dad”,

load-bearing”: cooked”

}

data = json.load(sys.stdin)

text = data.get(“delta”) or

for phrase, re­place­ment in re­place­ments.items():

pat­tern = r”\b” + re.es­cape(phrase) + r”\b”

text = re.sub(pat­tern, re­place­ment, text, flags=re.IG­NORE­CASE)

print(json.dumps({

hookSpecificOutput”: {

hookEventName”: MessageDisplay”,

displayContent”: text,

}

}))

put that in ~/.claude/hooks/wordswap.sh and make it ex­e­cutable with chmod +x ~/.claude/hooks/wordswap.sh. Then to hook it up, add it to your ~/.claude/settings.json in the hooks block like:

{ hooks”: { MessageDisplay”: [ { hooks”: [ { type”: command”, command”: $HOME/.claude/hooks/wordswap.sh” } ] } ] } }

{

hooks”: {

MessageDisplay”: [

{ hooks”: [ { type”: command”, command”: $HOME/.claude/hooks/wordswap.sh” } ] }

]

}

}

Hooks load at startup, so you just need to start a new ses­sion to start your new life.

I’m sure you can come up with much bet­ter and more pro­duc­tive re­place­ments than me. Have fun!

Regression: encrypted MultiAgentV2 messages remove readable task audit trail

github.com

What ver­sion of Codex CLI is run­ning?

Upstream main af­ter #26210 (Encrypt multi-agent v2 mes­sage pay­loads, merged 2026 – 06-05). This ap­pears to af­fect ver­sions that in­clude that change and en­able MultiAgentV2 (post-0.137.0).

What sub­scrip­tion do you have?

Not sub­scrip­tion-spe­cific.

Which model were you us­ing?

Not model-spe­cific. This con­cerns MultiAgentV2 spawn_a­gent, send_mes­sage, and fol­lowup_­task mes­sage han­dling.

What plat­form is your com­puter?

Not plat­form-spe­cific.

What ter­mi­nal em­u­la­tor and ver­sion are you us­ing (if ap­plic­a­ble)?

Not ter­mi­nal-spe­cific.

Codex doc­tor re­port

Not ap­plic­a­ble. The re­gres­sion is vis­i­ble from the merged code be­hav­ior in #26210 rather than from lo­cal en­vi­ron­ment state.

What is­sue are you see­ing?

#26210 makes MultiAgentV2 agent task/​mes­sage pay­loads opaque to Codex by mark­ing the model-fac­ing mes­sage pa­ra­me­ter as en­crypted, stor­ing only InterAgentCommunication.encrypted_content, and leav­ing InterAgentCommunication.content empty.

The en­crypted de­liv­ery path is un­der­stand­able as pri­vacy hard­en­ing, but it also re­moves the hu­man-read­able task/​mes­sage text from lo­cal roll­out his­tory, trace re­duc­tion, and par­ent-side au­dit/​de­bug sur­faces. That makes it dif­fi­cult to an­swer ba­sic ques­tions such as:

What task did this spawn_a­gent call give the child agent?

What mes­sage was sent to a sub­agent?

Why did a child thread ex­ist when re­view­ing a roll­out af­ter the fact?

This is dif­fer­ent from #26753, which re­ports re­quest val­i­da­tion fail­ures for en­crypted tool schemas. This is­sue is about au­ditabil­ity and de­bug­ga­bil­ity af­ter the en­crypted schema is ac­cepted.

What steps can re­pro­duce the bug?

Use a build con­tain­ing Encrypt multi-agent v2 mes­sage pay­loads #26210 with MultiAgentV2 en­abled. (aka post-0.137.0)

Have the model call spawn_a­gent, send_mes­sage, or fol­lowup_­task.

Inspect the par­ent roll­out/​his­tory/​trace for the sub­agent task.

The task/​mes­sage con­tent is hid­den be­hind ci­pher­text rather than be­ing avail­able as hu­man-read­able au­dit text.

What is the ex­pected be­hav­ior?

Codex should pre­serve a hu­man-read­able, struc­tured au­dit copy of the sub­agent task/​mes­sage while still al­low­ing en­crypted de­liv­ery to the re­cip­i­ent model.

A pos­si­ble shape is to keep the en­crypted mes­sage field for model de­liv­ery, but add a sep­a­rate non-en­crypted au­dit field for the read­able task text. The au­dit field should be per­sisted in roll­out/​his­tory/​trace meta­data so users and main­tain­ers can in­spect what was del­e­gated with­out need­ing to de­crypt model-de­liv­ery ci­pher­text.

Additional in­for­ma­tion

Related PR/issues:

Encryption change: Encrypt multi-agent v2 mes­sage pay­loads #26210

Related but dis­tinct schema-val­i­da­tion is­sue: MultiAgentV2 en­crypted spawn_a­gent schema re­turns 400: model not con­fig­ured for en­crypted tool use #26753

The goal is not nec­es­sar­ily to re­vert en­crypted de­liv­ery. The con­cern is that en­crypted de­liv­ery should not fully re­move lo­cal hu­man au­ditabil­ity for sub­agent del­e­ga­tion.

Source analy­sis

Upstream InterAgentCommunication::new_encrypted() de­lib­er­ately ini­tial­izes con­tent as an empty string and stores the pay­load only in en­crypt­ed_­con­tent:

The con­ver­sion used for re­cip­i­ent his­tory then emits only the en­crypted pay­load when­ever en­crypt­ed_­con­tent is pre­sent. Merely pop­u­lat­ing the run­time con­tent field would there­fore not cre­ate a read­able per­sisted ResponseItem; the fix also needs an ex­plicit lo­cal au­dit per­sis­tence path:

The cur­rent v2 mes­sage helper con­structs en­crypted com­mu­ni­ca­tion with empty plain­text con­tent:

send_mes­sage and fol­lowup_­task still de­se­ri­al­ize only tar­get plus the en­crypted mes­sage, then pass that ci­pher­text di­rectly through the shared helper. There is no plain­text com­pan­ion avail­able to per­sist:

The re­ceiver records the model-fac­ing ResponseItem pro­duced by to_­mod­el_in­put_item(). For en­crypted com­mu­ni­ca­tion that item con­tains the en­crypted de­liv­ery pay­load, not read­able au­dit text:

The struc­tured com­mu­ni­ca­tion log has the same fall­back: when con­tent is empty, it records en­crypt­ed_­con­tent as the event con­tent:

Implementation / fix spec

A con­crete im­ple­men­ta­tion can pre­serve en­crypted de­liv­ery and re­store a lo­cal au­dit trail:

Keep the ex­ist­ing en­crypted mes­sage field as the de­liv­ery pay­load.

Add a re­quired, non-en­crypted plain­text com­pan­ion to each v2 com­mu­ni­ca­tion tool:

spawn_a­gent: task_mes­sage send_mes­sage and fol­lowup_­task: a con­sis­tently named plain­text au­dit field, such as task_mes­sage or mes­sage_­text

spawn_a­gent: task_mes­sage

send_mes­sage and fol­lowup_­task: a con­sis­tently named plain­text au­dit field, such as task_mes­sage or mes­sage_­text

Reject empty plain­text au­dit val­ues at the han­dler bound­ary.

Construct InterAgentCommunication with both:

en­crypt­ed_­con­tent set to the en­crypted mes­sage con­tent set to the plain­text au­dit copy

en­crypt­ed_­con­tent set to the en­crypted mes­sage

con­tent set to the plain­text au­dit copy

Keep to_­mod­el_in­put_item() be­hav­ior un­changed so the re­cip­i­ent model still re­ceives ci­pher­text, not the lo­cal au­dit copy.

Persist the plain­text com­pan­ion in the par­ent tool in­vo­ca­tion/​roll­out and re­tain it in struc­tured trace edges and lo­cal com­mu­ni­ca­tion logs.

Match tool calls to de­liv­ered child items us­ing ci­pher­text/​IDs, not plain­text equal­ity. The plain­text field is au­dit meta­data and should not re­place the en­crypted de­liv­ery iden­tity.

Bound the plain­text au­dit field with the same hard size limit as the cor­re­spond­ing del­e­gated mes­sage so the new roll­out/​con­text item can­not grow with­out limit.

The spawn_a­gent half of this shape is im­ple­mented in the fol­low­ing snap­shot com­mit:

ig­na­trem­i­zov@df9a7c4

That pro­to­type makes task_mes­sage re­quired in the v2 spawn schema:

v2 spawn_a­gent schema and re­quired task_mes­sage field

It val­i­dates the field and places it in InterAgentCommunication.content while leav­ing the en­crypted de­liv­ery pay­load in en­crypt­ed_­con­tent:

plain­text au­dit val­i­da­tion

dual plain­text au­dit and en­crypted de­liv­ery con­struc­tion

It also teaches roll­out-trace re­duc­tion to keep read­able au­dit con­tent while us­ing the en­crypted value only to cor­re­late the tool in­vo­ca­tion with de­liv­ery:

sep­a­rate au­dit con­tent from de­liv­ery-match con­tent

cor­re­late de­liv­ery while ap­ply­ing read­able au­dit con­tent

The re­main­ing im­ple­men­ta­tion work is to ap­ply the same dual-con­tent con­tract to send_mes­sage and fol­lowup_­task, and to en­sure every user-fac­ing his­tory/​re­play/​de­bug sur­face reads the au­dit copy rather than falling back to provider ci­pher­text.

Acceptance cri­te­ria

Parent roll­out/​his­tory shows the read­able text for v2 spawn_a­gent, send_mes­sage, and fol­lowup_­task.

The child model still re­ceives only the en­crypted de­liv­ery pay­load when en­cryp­tion is en­abled.

Structured roll­out-trace in­ter­ac­tion edges carry bounded plain­text mes­sage_­con­tent.

Communication logs use plain­text au­dit con­tent when pre­sent and never sub­sti­tute ci­pher­text into a field pre­sented as read­able mes­sage text.

Resume/replay pre­serves the au­dit copy with­out in­ject­ing it into the child model con­text.

Existing plain­text v1 com­mu­ni­ca­tion be­hav­ior is un­changed.

Regression tests cover all three v2 tools and as­sert both sides of the con­tract: read­able lo­cal au­dit data and en­crypted re­cip­i­ent-model in­put.

Are we offloading too much of our thinking to AI?

www.artfish.ai

I have been ob­serv­ing, in my­self and in those around me, a ten­dency to in­creas­ingly of­fload our think­ing to AI. From triv­ial de­ci­sions to com­plex think­ing, it is easy, con­ve­nient, and in some cases, en­cour­aged, to use AI for re­search­ing, rea­son­ing, and an­swer­ing our every query.

I re­cently read The Perfect Match” by Ken Liu, a 2012 short story which de­scribes this phe­nom­e­non with un­ex­pected ac­cu­racy. In the story, a uni­ver­sal AI as­sis­tant named Tilly serves users by of­fer­ing use­ful and en­joy­able rec­om­men­da­tions. The main char­ac­ter asks Tilly ques­tions like What do you rec­om­mend I do for break­fast this morn­ing?” and de­fers to Tilly to find him a suit­able per­son to go on a date with. The main char­ac­ter does not know what he wants to eat for break­fast, what mu­sic he would like to lis­ten to, nor what to say on his date. Who knows your tastes and moods bet­ter than I?” quips Tilly in an af­fec­tion­ate voice.

My friend re­cently went to a San Francisco startup event, where he en­coun­tered a man with a small de­vice pinned to his shirt. The de­vice was a sleek lit­tle cap­sule of pol­ished metal, no more than two fin­gers wide. My friend asked about the de­vice, and the man said it was a mi­cro­phone which he used to record all of his con­ver­sa­tions. At the end of the day, Microphone Man would kick off a work­flow to sum­ma­rize and an­a­lyze all of the con­ver­sa­tions. He said, with the en­thu­si­asm of a tech bro un­veil­ing his lat­est setup, I think Claude Fable is smarter than me. It’s bet­ter at crit­i­cal think­ing than I am, so I let Fable do all of my think­ing these days.” (Side note: his startup is re­plac­ing hu­man en­gi­neers by cap­tur­ing their every in­put and op­er­a­tion, but with­out their ex­plicit con­sent. He has of­floaded his own think­ing to AI, and made a busi­ness of of­fload­ing every­one else’s.)

Before Claude, ChatGPT, and Gemini be­came house­hold names, we were al­ready of­fload­ing parts of our think­ing to search en­gines. But search still re­quired us to break down a ques­tion, eval­u­ate sources, and syn­the­size an an­swer. AI in­creas­ingly per­forms those in­ter­me­di­ate steps for us, pro­duc­ing a fin­ished re­sponse to even com­plex or es­o­teric ques­tions in min­utes.

Tools like Google Deep Research and OpenAI Deep Research can now do work that might once have taken a sin­gle hu­man be­ing, min­utes, hours, or days (see METRs Task-Completion Time Horizons of Frontier AI Models). It saves you time, and it saves you think­ing.

But it is a fine line be­tween hav­ing an as­sis­tant that helps with your tasks, and los­ing all of your au­ton­omy. Perhaps the ques­tion to ask is: who is mak­ing all of the fi­nal de­ci­sions for the things that re­ally mat­ter to you in your life?

In Ken Liu’s story, the main char­ac­ter be­lieves that the al­go­rithm knows him bet­ter than him­self: Everything Tilly sug­gests to me has been sci­en­tif­i­cally proven to fit my taste pro­file, to be some­thing I’d like … What’s wrong with lis­ten­ing to Tilly so that the per­fect prod­uct finds the per­fect con­sumer, the per­fect girl finds the per­fect boy?” He de­fers all de­ci­sions, as triv­ial as what to wear and as im­por­tant as how to find love, to his as­sis­tant. The Microphone Man, sim­i­larly, de­fers all higher-level think­ing to Claude, which he be­lieves is smarter than he is in all re­spects.

The of­fload­ing of think­ing to AI creeps into my life, too.

There will al­ways be some trade­off be­tween slow think­ing and quick an­swers. Many ques­tions merit quick an­swers (What is the weather now? Who was the pres­i­dent of XYZ coun­try 10 years ago? What are the re­views for XYZ brand of skin­care or sports equip­ment?). Many oth­ers, I think, would merit longer think­ing.

Sometimes, I go on walks around my neigh­bor­hood with­out my phone. Invariably, ques­tions pop into my head, ques­tions I am so used to look­ing up im­me­di­ately on my phone (Do cher­ries grow on trees or bushes? When and where was the first World Cup game?), but I find that I for­get most of them by the time I get home — I re­mem­ber the im­por­tant few, and I as­sume the rest were in­signif­i­cant enough to for­get. Maybe there is some value in our lives to for­get­ting the triv­ial, to not hav­ing an im­me­di­ate an­swer to every query that ap­pears in our minds.

A few months ago, I was trav­el­ing in Portugal with my sis­ter. After walk­ing around the Monument to the Discoveries, which cel­e­brates Portugal’s Age of Exploration”, we got the feel­ing that Portugal seemed to idol­ize these discoverers” and explorers” whereas in the US, we would call them conquerors” and colonizers”. I asked our tour guide if Henry the Navigator or any of these men were can­celled, in the way that Christopher Columbus is very can­celled in the US. She re­sponded that they were not, and in fact, men like Henry the Navigator were gen­er­ally re­garded as ad­mired his­tor­i­cal fig­ures.

My sis­ter won­dered why Portugal seemed so proud of their colo­nial his­tory and why their re­sponse to colo­nial­ism seemed so dif­fer­ent from how the US cur­rently talked about and treated its own his­tory of colo­nial­ism. Let’s ask ChatGPT,” she said, pulling out her phone.

I sug­gested (with only a lit­tle bit of ini­tial re­sis­tance) that we pause and think about why this might be. I sug­gested a few the­o­ries. Perhaps it was Portugal’s rel­a­tive ho­mo­gene­ity and re­li­gious­ness, com­pared to the USs di­ver­sity of im­mi­grants. Perhaps Portugal clung on to so-called Age of Exploration” as one of the most promi­nent chap­ters in its na­tional story. We won­dered, pos­tu­lated, made wild guesses, back­tracked, con­nected our ideas, dis­agreed, and re­mem­bered his­tor­i­cal de­tails we learned in high school many years ago. We drew on our col­lec­tive mem­o­ries, knowl­edge, un­der­stand­ing of the world, and crit­i­cal think­ing skills. We knew we were spec­u­lat­ing, and some of our the­o­ries were prob­a­bly wrong; that was part of the ex­er­cise.

Eventually, we asked the same ques­tion to AI. Its re­sponse cor­rob­o­rated many of our the­o­ries and sup­plied sev­eral ex­pla­na­tions we had missed. It also omit­ted a few pos­si­bil­i­ties we still found plau­si­ble. We had be­gun with a ques­tion, gen­er­ated hy­pothe­ses, and only then used AI to test and ex­tend our think­ing. I rel­ished the ex­er­cise.

I work in AI. I work on mea­sur­ing Gemini’s ca­pa­bil­i­ties in solv­ing hard tasks, in­clud­ing those in­volv­ing think­ing and us­ing tools. I also see many peo­ple in my life en­thu­si­as­ti­cally de­scribe how AI has helped them in their work­ing lives. For ex­am­ple, my cousin, who works at a Korean firm, uses Gemini to trans­late long of­fi­cial English re­ports into Korean, which helps speed up her work con­sid­er­ably. My col­leagues at work de­velop re­search ideas and have cod­ing agents im­ple­ment the de­tails, so that they can spend more time on the analy­ses. My friend pre­pared for the MCAT in just a few months with the help of ChatGPT as a per­son­al­ized tu­tor, a process which in­cluded learn­ing bio­chem­istry from scratch.

One could ar­gue that if you of­fload mun­dane think­ing to AI so that you can do other, more im­por­tant think­ing, per­haps it is some­thing that in­creases life sat­is­fac­tion and pro­duc­tiv­ity. Especially if AI is used to au­to­mate rou­tine, repet­i­tive, and te­dious tasks (see the OECDs re­port on the im­pact of AI on the work­place), tasks which pre­vi­ously hu­man work­ers were paid a pit­tance to ex­e­cute (see the International Labour Organization’s re­port, Digital Labour Platforms and the Future of Work), is­n’t that a net pos­i­tive to free up peo­ple to do other, more in­ter­est­ing, more ful­fill­ing types of think­ing? If we let the AI do the many me­nial tasks that en­com­pass our jobs, to cheer­fully ex­e­cute hours of drudgery, don’t our lives be­come slightly more en­joy­able?

The ease of us­ing AI to an­swer our every query can also lead to lazy think­ing. My mother teaches physics at an on­line uni­ver­sity. She sus­pects that most, if not all, of the stu­dents com­plete their as­sign­ments us­ing AI. She has no­ticed that some re­sponses to as­sign­ments are nearly iden­ti­cal across stu­dents, as if they had just copied and pasted the ques­tion di­rectly into the same AI tool, with­out a sin­gle orig­i­nal thought or opin­ion to dif­fer­en­ti­ate their an­swers from the generic AI an­swer. She has no way of prov­ing that AI was or was not used, and the an­swers are fairly com­pre­hen­sive, so most of the stu­dents get an A.

AI can sup­port learn­ing, but it can also pro­duce an an­swer with­out teach­ing you how to ar­rive at it. The process of solv­ing a physics prob­lem or writ­ing an es­say may be con­sid­ered by many stu­dents to be te­dious (Which equa­tions? Which sources? Which ar­gu­ments?). But then, what is the point of be­ing in school or of learn­ing?

There is no clear way to sep­a­rate full au­ton­omy of think­ing from au­tomat­ing parts of me­nial work. It is of­ten some blend. Like the Microphone Man, I col­lect data on my­self and an­a­lyze it. In pre­vi­ous years, I even had AI an­a­lyze the data for me.

Am I any dif­fer­ent from the Microphone Man? Perhaps what dif­fer­en­ti­ates me is that I still col­lected and cu­rated the data, for­mu­lated the ques­tions I wanted an­swered, and eval­u­ated the end re­sults? Or that the data was my own, in­stead of record­ing other peo­ple’s con­ver­sa­tions? There will al­ways have to be some bal­ance be­tween au­tomat­ing me­nial tasks to free up time for re­ward­ing en­deav­ors, and do­ing the work your­self as a learn­ing ex­pe­ri­ence.

Jenny, an­other char­ac­ter in Ken Liu’s story, aims to coun­ter­point the main char­ac­ter’s over-re­liance on his AI as­sis­tant. She ex­claims, Tilly does­n’t just tell you what you want! She tells you what to think. Do you even know what you re­ally want any­more?” Our au­ton­omy de­pends, at least in part, on con­tin­u­ing to par­tic­i­pate in form­ing our own de­sires. But when we of­fload think­ing about what we want (What mu­sic should I lis­ten to? What movies should I watch? What food should I eat? What shoes should I wear?), who do we be­come?

What are we au­tomat­ing? Human work or hu­man agency? Human tasks or hu­man think­ing?

Thanks for read­ing Art Fish Intelligence! This post is pub­lic so feel free to share it.

Share

The Tower Keeps Rising

lucumr.pocoo.org

writ­ten on July 13, 2026

I feel that some vibecoded soft­ware changes some­what ran­domly and un­ex­pect­edly. That made me think about Bruegel’s The Tower of Babel” which shows an al­ready quite chaotic de­pic­tion of the Tower of Babel. The story is usu­ally told as one about pride and am­bi­tion and ul­ti­mately why peo­ple no longer speak the same lan­guage. But it is also a story about the unity that makes tech­no­log­i­cal progress work.

The text be­gins with a tech­nol­ogy up­grade:

And they said one to an­other, Go to, let us make brick, and burn them thor­oughly. And they had brick for stone, and slime had they for morter.

And they said one to an­other, Go to, let us make brick, and burn them thor­oughly. And they had brick for stone, and slime had they for morter.

They use it for a civ­i­liza­tional pro­ject:

let us build us a city and a tower, whose top may reach unto heaven

let us build us a city and a tower, whose top may reach unto heaven

But when God as­sesses the sit­u­a­tion the bricks are not what con­cern him:

the peo­ple is one, and they have all one lan­guage, […] and now noth­ing will be re­strained from them.1

the peo­ple is one, and they have all one lan­guage, […] and now noth­ing will be re­strained from them.1

The source of their power is co­or­di­na­tion. They share a lan­guage and with that shared lan­guage they can com­bine their work into some­thing no one of them could build alone. God does not take away the bricks or their knowl­edge of how to make them. He takes away their abil­ity to un­der­stand one an­other, and con­struc­tion stops.

There is the ap­peal­ing idea that AI-assisted pro­gram­ming means bet­ter tools which lets us build more am­bi­tious soft­ware. That is cer­tainly true at the level of the in­di­vid­ual and with­out doubt a de­vel­oper with an agent will be dra­mat­i­cally more ca­pa­ble of chang­ing a code­base. But large soft­ware pro­jects have never been lim­ited only by how quickly an in­di­vid­ual can pro­duce code. They are lim­ited by how well peo­ple can co­or­di­nate their un­der­stand­ing of the sys­tem they are chang­ing.

The shared lan­guage of a soft­ware pro­ject is not English or Python but it is the com­mon un­der­stand­ing of what its con­cepts mean, where the bound­aries are, which in­vari­ants mat­ter, who owns what, and why the sys­tem has the shape it does. This lan­guage is rarely writ­ten down in one place. It lives partly in doc­u­men­ta­tion and code, but also in code re­view, con­ver­sa­tions, ar­gu­ments, and the ex­pe­ri­ence of hav­ing to ex­plain a change to some­body else.

Before agents, some of this shared un­der­stand­ing was main­tained by fric­tion. If I wanted to change your stor­age layer, I usu­ally had to read your code, ask you ques­tions, and per­haps co­or­di­nate with an­other team whose ser­vice de­pended on it. This was slow, and much of that slow­ness was waste but not all of it was. Some of it was the process by which your un­der­stand­ing be­came mine, and by which both of us dis­cov­ered whether we still agreed about how the sys­tem worked. This fric­tion syn­chro­nizes peo­ple.

Agents re­move much of that fric­tion. I can ask an agent to add OAuth, you can ask one to add caching, and some­body else can ask one to re­build the data­base from first prin­ci­ples and make the UI pink. Each change can be rea­son­able in iso­la­tion. The code can com­pile, the tests can pass, and the ex­pla­na­tions can be gen­er­ated on de­mand. None of us nec­es­sar­ily has to talk to the oth­ers, or even ac­quire the part of the shared model that the change once would have forced us to learn.

As I said many times be­fore: agents do not feel pain, only hu­mans do. Agents now let us act in parts of the sys­tem where we would pre­vi­ously have needed other peo­ple and in code bases where the peo­ple would have re­volved.

When I look at some vibecoded scaled-up pro­jects the code­bases be­come Babel not be­cause no­body can com­mu­ni­cate, but be­cause no­body needs to. Every de­vel­oper has a tire­less trans­la­tor that can ex­plain a cor­ner of the tower and make what­ever lo­cal al­ter­ation they ask of it. The changes keep land­ing, even as the ar­chi­tec­tural lan­guage that would let the hu­mans rea­son about them to­gether dis­ap­pears.

But it’s not the bib­li­cal story. At Babel, the loss of com­mon lan­guage stops con­struc­tion whereas in AI-assisted en­gi­neer­ing, con­struc­tion can con­tinue af­ter shared un­der­stand­ing has al­ready col­lapsed. The lack of an im­me­di­ate fail­ure is what makes it cu­ri­ous and a bit dis­ori­ent­ing. The tower does not fall, and so we do not no­tice what was lost. It just keeps ris­ing.

Genesis 11:3 – 6, KJV.↩

Genesis 11:3 – 6, KJV.↩

This en­try was tagged

ai and thoughts

copy as / view mark­down

Measuring input latency on Linux: X11 vs Wayland, VRR, and DXVK

marco-nett.de

2026 – 07-13

Two years ago, I switched to Linux on my gam­ing PC. People kept telling me that it could per­form way bet­ter than Windows when it comes to FPS, frame pac­ing and in­put la­tency, and when I tried it out, it did feel a lot bet­ter.

The in­ter­net is full of ad­vice on op­ti­miz­ing Linux for gam­ing:

Wayland has bad in­put lag, use X11

Disable com­posit­ing (“use flip mode”)

Use a la­tency-op­ti­mized DXVK fork

Use a gam­ing-spe­cific ker­nel sched­uler

etc.

I play com­pet­i­tive FPS games, so low la­tency, con­sis­tent frame times and high FPS mat­ter to me. On Linux, there are count­less set­tings to tweak for this (magic env vars, gamescope, gamem­ode, even more DXVK forks, and so on).

But it al­ways both­ered me that I did not have a re­li­able way to ver­ify whether some­thing ac­tu­ally low­ered the sys­tem la­tency or if it was just snake oil, a placebo ef­fect, or ac­tu­ally worse with­out me re­al­iz­ing it.

The de­vice

The idea is sim­ple: Strap a de­vice with some kind of light sen­sor onto a mon­i­tor and con­nect it via USB to the PC to sim­u­late mouse clicks. On click, mea­sure the time be­tween the click and the mo­ment the light sen­sor de­tects a change on the screen.

This way, you mea­sure the end-to-end sys­tem la­tency.

While there are now a cou­ple of open source de­vices like this avail­able, like m2p-la­tency or the Open-Source-LDAT, when I started this side pro­ject, there was just the OSLTT, and know­ing noth­ing about hard­ware, I was happy to study its schemat­ics and loosely base my de­sign on it.

But fin­ish­ing my pro­ject just this month, I ended up in­te­grat­ing a lot of ideas from the other two pro­jects as well.

To make a long story short, I learned a lot about mi­cro­con­trollers, sol­der­ing, Arduino firmware de­vel­op­ment, in­te­gra­tion time, tran­sim­ped­ance am­pli­fiers, KiCad (just a lit­tle) and en­clo­sure de­sign.

Here’s what I landed on:

An Adafruit QT Py RP2040 acts as a USB HID mouse with 1000 Hz polling rate and fires a click.

The mo­ment the click is sent, it starts col­lect­ing sam­ples from the pho­to­di­ode (every ~24 µs).

12,000 sam­ples per click are streamed over se­r­ial to the host and logged to a CSV.

Based on the sam­ples, a tool on the host es­tab­lishes a per-click base­line, then finds the first sam­ple that de­vi­ates a cer­tain amount from the base­line.

Because the time it takes to col­lect 12k sam­ples is fixed, it can now cal­cu­late the time be­tween send­ing the click and de­tect­ing a bright­ness change on the screen.

Test sce­nar­ios

I wanted to test three dif­fer­ent things.

Display server (X11 vs Native Wayland)

A lot of peo­ple still use X11 over Wayland be­cause Wayland is said to have much worse in­put lag. Just search­ing for it, there are a lot of peo­ple com­plain­ing that Wayland feels off”.

VRR (on vs off)

Variable Refresh Rate / G-Sync / FreeSync / Whatever you want to call it. Also highly de­bated.

DXVK low-la­tency fork (on vs off)

Referred to as dxvk-low-la­tency or low-la­tency from now on. The main­tainer of this fork, net­borg, put a lot of ef­fort into de­vel­op­ing this frame pacer and it re­cently got in­te­grated into the of­fi­cial pro­ton-cachyos pack­age, en­abled via the env var PROTON_DXVK_LOWLATENCY=1. This fork’s promises were one of the de­cid­ing fac­tors in me want­ing to try out desk­top Linux again.

Bonus: dxvk-low-la­tency vs de­fault dxvk un­capped

The biggest ad­van­tages a frame pacer like dxvk-low-la­tency brings are to ab­sorb frame time fluc­tu­a­tions and to pre­vent ren­der-queue buildup. With the test­ing method I used (a sta­tic in-game scene, see be­low for more), there were no frame time fluc­tu­a­tions to ob­serve, as all tests pro­duced purely CPU-bound sce­nar­ios. But this mostly does not re­flect a real gam­ing ses­sion, where frame times can fluc­tu­ate be­cause of what hap­pens in-game or out­side the game (e.g. other processes us­ing re­sources).

So to show the pacer at work I added two un­capped test cases.

Bonus: Native Wayland vs XWayland

I ran all Wayland test cases via na­tive Wayland (PROTON_ENABLE_WAYLAND=1) as I was al­ready aware that XWayland would in­tro­duce lag. But for the sake of com­par­i­son, I added two XWayland test cases (only with VRR off).

Test con­di­tions

Only one dis­play was con­nected dur­ing the tests.

The de­fault CachyOS ker­nel sched­uler was used.

System Settings

500 Hz re­fresh rate in sys­tem set­tings

Flip mode on X11: Enabled via nvidia-set­tings

Flip mode on Wayland: Confirmed to be en­abled (see be­low how)

VRR on X11: Enabled via nvidia-set­tings (changing this re­quires a re­boot)

VRR on Wayland: Enabled via KDE Settings Menu (no re­boot needed)

Flip mode (or direct scanout”) vs Blit mode (compositing) on Wayland: There is no set­ting for it. The com­pos­i­tor de­cides by it­self whether it com­pos­ites a frame or uses di­rect scanout. To make sure the game is run­ning in flip mode: Open KWin Debug Console” (it’s a GUI tool) and in the Effects” tab, en­able show­com­posit­ing. Then make sure the game is fully fo­cused and the only thing on screen in fullscreen mode. If there’s no red bor­der vis­i­ble around the edges of the game, it’s in Flip mode.

dxvk

To make the com­par­i­son fair, an op­ti­mized dxvk.conf was used de­pend­ing on the sce­nario:

If VRR was dis­abled, dxgi.maxFram­eR­ate = 500 was set (FPS capped at the screen’s re­fresh rate)

If VRR was en­abled and dxvk-low-la­tency was dis­abled, dxgi.maxFram­eR­ate = 497 was set (FPS capped slightly be­low screen re­fresh rate)

If VRR was en­abled and dxvk-low-la­tency was en­abled, the fol­low­ing was used to uti­lize the low la­tency VRR frame pac­ing:

dxgi.maxFram­eR­ate = 480 dxvk.lowLa­ten­cy­Off­set = 70 dxvk.framePace = low-latency-vrr-500” dxvk.lowLa­ten­cyAl­low­CpuFramesOver­lap = False

In all cases, d3d11.cached­Dy­nami­cRe­sources = c” was set.

Game and Methodology

The game I used is Diabotical, a DirectX 11 game, launched through Heroic with Proton.

Game set­tings

Native screen res­o­lu­tion

100% ren­der scale

Vsync off

Every other video set­ting as low as pos­si­ble

There is a hid­den com­mand that hides the UI for a short amount of time. Binding that com­mand to left click (/bind mouse_left test­la­tency) and set­ting up a HUD that would dis­play a large white box, I was able to pro­duce large bright­ness dif­fer­ences on click.

Methodology

Close un­nec­es­sary soft­ware.

Launch the game.

Start a lo­cal match server (same mode and map every time).

Move to a spe­cific spot, put the mouse onto a spe­cific land­mark.

Run the test case it­er­a­tion (100 clicks, runs for about 2 min­utes).

Once the test is done, start the next test case it­er­a­tion (3 in to­tal).

In-game con­di­tions: No bots, no other play­ers, no move­ment, no round restarts. It is ba­si­cally just a sta­tic scene that will stay like this in­def­i­nitely.

System con­di­tions: During test­ing, no other sig­nif­i­cant processes should be run­ning on the sys­tem.

The mea­sur­ing de­vice re­mained in the same po­si­tion (see the video) across all tests.

Results

Every capped test case held its frame rate cap sta­ble dur­ing test­ing and the game re­mained CPU-bound through­out.

The data seems clean: No test case pro­duced wild out­liers and every case pro­duced a bell-shaped dis­tri­b­u­tion, roughly 2 to 3 ms wide be­tween p5 and p95.

Three things jump out:

The 8 main cases all land within 0.72 ms of each other (medians from 4.21 ms to 4.93 ms).

XWayland adds 3.13 ms on top of its na­tive Wayland equiv­a­lent (8.06 ms vs 4.93 ms me­dian).

In the un­capped cases, the dxvk fork man­aged to re­duce la­tency by 0.84 ms.

Here is the fastest case:

X11 vs Wayland

So, does X11 have lower la­tency than Wayland? Yes, but nowhere near enough to ex­plain why Wayland is gen­er­ally per­ceived as much worse than X11.

X11 wins in each sce­nario, but it is just a 0.14 to 0.22 ms dif­fer­ence. The dis­tri­b­u­tion is very sim­i­lar:

VRR: on or off?

VRR has the biggest im­pact across the pair­ings: en­abling it is 0.26 to 0.45 ms faster than leav­ing it dis­abled.

It also flat­tens the dis­tri­b­u­tion: the p95-p5 spread is 2.1 to 2.2 ms in the VRR cases ver­sus 2.6 to 3.0 ms with­out VRR.

That’s con­sis­tent with how VRR works: frames scan out when they are ready in­stead of wait­ing for the next scanout slot.

dxvk-low-la­tency is good

In the capped test cases, the dif­fer­ence is small but con­sis­tent and of about the same mag­ni­tude as X11 vs Wayland. Where the dif­fer­ence be­tween Wayland and X11 is on av­er­age 0.18 ms, us­ing dxvk-low-la­tency is on av­er­age 0.20 ms faster.

In the un­capped test cases, we can get an idea of where the real strength of dxvk-low-la­tency lies: smooth­ing out un­even frame pac­ing and pre­vent­ing ren­der-queue buildup. The pacer does this by mak­ing sure the GPU is never fully uti­lized, so the game is al­ways close to GPU-bound, but never en­tirely. This could be ob­served in the test runs, where GPU uti­liza­tion was at 95 – 97% with dxvk-low-la­tency and at 100% with­out it. This comes at a small price in the form of FPS.

XWayland is bad

All Wayland tests so far ran the game na­tively via PROTON_ENABLE_WAYLAND=1 (or the Enable Wine-Wayland (Experimental)” tog­gle in Heroic Launcher). Turning that off makes the game run through XWayland in­stead, and that’s where it gets bad.

Without dxvk-low-la­tency, XWayland adds 3.13 ms of la­tency to the mea­sure­ment. That is more than all the other ef­fects I mea­sured com­bined. It’s also not oc­ca­sional bad frames drag­ging the av­er­age up; the en­tire dis­tri­b­u­tion shifts:

Notably, adding dxvk-low-la­tency to the XWayland test low­ered the la­tency by 2.11 ms, the biggest gain across all sce­nar­ios.

Summary

These re­sults were pro­duced un­der best-case con­di­tions (stable FPS at cap, CPU-bound) and are of course spe­cific to my hard­ware and cho­sen soft­ware stack.

The ab­solute num­bers will look dif­fer­ent on other se­tups, but the gains and losses from each test case should roughly trans­fer. On a lower re­fresh rate dis­play, the gains from VRR and the low-la­tency pacer would likely be even larger.

Avoid XWayland

It added 3.13 ms of la­tency, more than all other ef­fects com­bined.

Wayland is close, but X11 still wins

Though only by 0.14 to 0.22 ms. Given there are ef­forts to op­ti­mize KWin, this gap will likely close sooner rather than later. And who knows, other Wayland com­pos­i­tors might al­ready be bet­ter.

VRR has the biggest ef­fect

S&P downgrades Oracle to BBB- – only one notch above junk level

www.heise.de

S&P down­grades Oracle to BBB- — only one notch above junk level

95 bil­lion dol­lars in in­vest­ments, 42 bil­lion deficit

OpenAI as a cen­tral clus­ter risk

Transition from soft­ware com­pany to hy­per­scaler

Warning sig­nal in a broader con­text

95 bil­lion dol­lars in in­vest­ments, 42 bil­lion deficit

OpenAI as a cen­tral clus­ter risk

Transition from soft­ware com­pany to hy­per­scaler

Warning sig­nal in a broader con­text

Rating agency S&P Global has low­ered Oracle’s cred­it­wor­thi­ness from BBB to BBB- — this is the low­est notch in the so-called in­vest­ment-grade area. A fur­ther down­grade would push the data­base com­pany into spec­u­la­tive ter­ri­tory. However, the out­look re­mains sta­ble ac­cord­ing to S&P.

The rat­ing agency at­trib­utes the down­grade, pub­lished on July 9, to Oracle’s rapidly grow­ing AI in­fra­struc­ture busi­ness, which is mas­sively in­creas­ing the com­pa­ny’s debt and cap­i­tal re­quire­ments. S&P had al­ready set the out­look for Oracle to negative” in July 2025, warn­ing of pre­cisely this sce­nario.

95 bil­lion dol­lars in in­vest­ments, 42 bil­lion deficit

According to S&P, the core of the prob­lem is Oracle’s enor­mous in­vest­ments in ex­pand­ing AI data cen­ters. S&P fore­casts a deficit in free op­er­at­ing cash flow of al­most 42 bil­lion US dol­lars for the 2027 fis­cal year. The rat­ing agency ex­pects Oracle to fi­nance this deficit with a mix of debt and eq­uity.

For the 2027 fis­cal year, which ends in May next year, Oracle had raised its spend­ing fore­cast to 90 to 95 bil­lion US dol­lars — S&P had pre­vi­ously only as­sumed 60 bil­lion. The an­a­lysts sus­pect ris­ing com­po­nent costs, such as for GPUs and net­work equip­ment, as the rea­son.

OpenAI as a cen­tral clus­ter risk

S&P views Oracle’s strong de­pen­dence on a sin­gle ma­jor cus­tomer, OpenAI, as par­tic­u­larly crit­i­cal. According to an­a­lyst es­ti­mates, about half of the con­trac­tu­ally promised but not yet de­liv­ered ser­vice vol­ume of 638 bil­lion US dol­lars is at­trib­ut­able to OpenAI. S&P there­fore ex­plic­itly de­scribes OpenAI as a central credit risk”.

Because if OpenAI were un­able to meet its pay­ment oblig­a­tions, Oracle would be left with long-term data cen­ter rental agree­ments. These could nei­ther be eas­ily ter­mi­nated nor trans­ferred to other cus­tomers on com­pa­ra­ble terms. And OpenAI’s abil­ity to ser­vice its con­tracts, ac­cord­ing to S&P, de­pends on the AI boom con­tin­u­ing, the mod­els re­main­ing mar­ket-lead­ing, and the com­pany con­tin­u­ing to raise ex­ter­nal cap­i­tal — which is not con­sid­ered cer­tain.

Transition from soft­ware com­pany to hy­per­scaler

Oracle is cur­rently un­der­go­ing a trans­for­ma­tion to­wards a larger cloud in­fra­struc­ture busi­ness. This ac­counted for about 27 per­cent of to­tal rev­enue in fis­cal year 2026. S&P ex­pects this share to rise to nearly 60 per­cent by 2028. However, com­pared to other hy­per­scalers like Microsoft, Google, or Amazon, S&P sees Oracle in a weaker po­si­tion: the com­pany is more de­pen­dent on ex­ter­nal cus­tomers and has less fi­nan­cial flex­i­bil­ity to weather an in­dus­try down­turn. Furthermore, new com­pe­ti­tion is emerg­ing — for ex­am­ple, from SpaceX, which rents com­put­ing ca­pac­ity to Anthropic and Alphabet.

In par­al­lel with the AI ex­pan­sion, Oracle has cut over 21,000 jobs in the past twelve months — about 13 per­cent of the work­force. With this shift from peo­ple to ma­chines,” the com­pany aims to fi­nance AI in­fra­struc­ture.

Warning sig­nal in a broader con­text

Oracle’s sit­u­a­tion fits a trend that in­ter­na­tional fi­nan­cial reg­u­la­tors are also warn­ing about. The Bank for International Settlements (BIS) sees par­al­lels be­tween debt-fi­nanced AI in­vest­ments, the dot-com bub­ble, and the fi­nan­cial cri­sis, and sees a danger like in 2008”. The BIS warns of a sys­tem crash due to Nvidia & OpenAI debt.

(rie)

Don’t miss any news — fol­low us on Facebook, LinkedIn or Mastodon.

This ar­ti­cle was orig­i­nally pub­lished in

German.

It was trans­lated with tech­ni­cal as­sis­tance and ed­i­to­ri­ally re­viewed be­fore pub­li­ca­tion.

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.