Submind YouTube summaries
Thumbnail for Open Source Maintenance, 2026-07-18

Open Source Maintenance, 2026-07-18

Watch on YouTube

Video summary

This maintenance stream focuses on resolving critical concurrency issues within the Rust crate `left-right` using ThreadSanitizer analysis. Two primary bugs were identified: first, a data race in `RightHandle::drop` where newly created read handles initialized with even atomic counters caused premature drops of shared structures; this was fixed by ensuring new counters are marked as odd to guarantee proper synchronization until all active reads complete. Second, the logic for handling re-entrant calls to `ReadHandle::enter` during a writer drop sequence was corrected from panicking on null states to safely returning `None`, preventing crashes when readers attempt entry after pointers have been swapped but before previous guards are released. These fixes were implemented locally and prepared for merge via pull requests due to repository access restrictions, alongside the resolution of failing tests in PR #149 caused by missing initial wait calls that led to out-of-bounds memory accesses. Attention then shifts to the `hdr-histogram` crate, where safety was prioritized over performance after an unsafe optimization left memory uninitialized; safe zero-initialization is now preferred despite minor costs. The batch API for calculating percentiles was significantly improved by replacing vector-based sorting with an iterator approach using closures and `from_fn`, which eliminates unnecessary allocations and handles unsorted or duplicate inputs gracefully. Performance gains were further achieved through a "chunked skip scan" technique that processes fixed-size bins in parallel before walking individual elements, enabling better vectorization. Concurrently, the Minimum Supported Rust Version (MSRV) was bumped from 1.64 to 1.88 to utilize newer features like `use` syntax and optimized iterators, while redundant "paranoid" tests were removed as existing coverage already ensures correctness. The maintenance workflow also addresses infrastructure challenges and project lifecycles, including the decision not to personally fix known security vulnerabilities in the abandoned `rust-imap` crate but instead facilitating a handover of ownership for an async rearchitecture under the same name. To manage CI failures caused by slow builds and missing secrets, the team switched from slower AI models to more efficient ones like Fable in Plan mode and updated dependencies systematically using automated agents. This approach highlights how AI tools are effectively utilized for mechanical tasks such as dependency updates and code modernization, allowing maintainers to focus on substantive architectural decisions rather than tedious manual labor involving constant log reviews and syntax updates. Finally, the discussion covers the ethical nuances of leveraging AI in open source, distinguishing between appropriate uses like debugging assistance and initial filtering versus inappropriate low-effort submissions that require significant rewriting. While acknowledging concerns about training data scraping public code, the speaker emphasizes that roles requiring deep system understanding remain secure against automation, whereas niche tasks are at risk. The session concludes by reflecting on the realities of maintaining multiple projects over extended periods, noting that even simple updates can take days due to community reliance and platform-specific testing constraints, before thanking viewers for their support in this ongoing effort to keep open source tools safe and efficient.
Read the full video transcript
Hey folks, welcome back to uh another Rust stream. Um this is going to be one of the open source maintenance streams. So um I've done a few of these in the past, although it's been a while since the last one. Um the intent behind these is well I maintained a bunch of different open source Rust crates. Um I maintain some non- Rust things too, although most of those sort of fallen by the wayside um at this point. But uh keeping up with the open source stuff takes a bunch of time. Um there there's a lot of like reviewing issues, reviewing PRs, fixing issues, doing releases, uh you know, updating dependencies are going to depend upon, fixing CI, like there's a bunch of that kind of maintenance that, um I think many people who are not used to being open source maintainers don't really see um they don't see it both in the sense of don't appreciate that it's happening and the maintainers have to spend time on it, but also they don't get to expose to it. So they don't know that those are things that if you are to release something in into open source you have to spend time on and it will consume some of the like it's not as though putting an open source library out there is just a matter of putting it out there. You also need to sort of sub subsist those projects over time. Um and this is an intent to sort of show a little bit of what that requires. Um and so in particular we're going to basically go through my GitHub notifications. I've already pruned a bunch of things of there. I mean, I'm obviously subscribed to a bunch of, you know, Rust issues and like tracking issues and stuff, things that are not that interesting to look through on stream. Um, so we're going to specifically look through for things that require like my maintainer attention. Uh, and then we'll just start walking through them and dealing with them as I would normally. Um, this is a task that, you know, I have to do once a week or ideally more often, but often I don't even have the time for once a week. So just somewhat regularly I have to sort of cycle through these and and uh keep up with PRs and issues and everything and you'll come along for the journey. Um now worth pointing out that one of the things that makes this uh challenging is that there are many different projects and so we're going to need to do quite a lot of context switching. Anytime we switch between repos there's going to be a bunch of context you don't have for that repository but that I have in my brain. So I'll do my best to try to carry you along here. Um, but just keep in mind that the the transitions might be a little jarring. Um, but I'll I'll do my best to to have that not happen. Um, and as always, like if if you have questions, put them in chat and then I'll I'll try to address them. If you're watching this after the fact, we'll put the questions I I'm in on a piece of paper and and make a paper airplane. I don't know. I don't know what to do about them. Um, but maybe you'll join the next stream and you can uh ask me live. Cool. So, um, as I pointed out, the one of the problems here is that the notification button on GitHub is behind the image of me on the screen, and I have a little danger box there so that I can see that things aren't visible to you. But that also means that I can't click on the notification thing. So, I need to go to notifications. Um, and then what I'm going to do is, and you can't see this offscreen either, but I'm going to sort from oldest to newest instead of newest to oldest because it tends to be the case that the oldest ones are the ones that most require my attention because well, I haven't addressed them in a while. Um, this also means that uh it's sort of a a slight punishment for people who keep bumping issues. Like if you are if you want me to reply to something, bumping it is not the way to do it because makes me less likely not more likely to look at the thing. Um, cool. So, let's uh group this roughly speaking by repository so that we don't end up switching back and forth a lot. Um, so you'll see there are a couple here from left to right. So, let's pull those out. Uh, and I think it's yeah, there's only four. So, for context here, left right I'll I'll um pull it up here first. So, left right is this uh concurrency primitive that I've given a a talk or two about this in the past. Um and it is a concurrency primitive that acts a little bit like a read or writer lock except it is a lot faster for reads but it is also slower and less memory efficient for writes. Um and so it sort of makes a slightly different tradeoffs in the in the in the concurrency performance space uh compared to something like a reader writer lock. Um, this one has a lot of like low-level um low-level like uh concurrency manipulation and atomics and stuff. Uh, but that's part of what makes it interesting. It's also part of what means that whenever I go back to look at this, there's always like issues or pull requests that are like very specific about some aspect of the the concurrency protocol. So, I always need to like bring it back into my brain in order to be able to address them. Um, so let's see. The first one is fix the data race on right handle drop. Um so if we pull up left right um you'll see that when you create a left right uh here for example uh you call left right new uh and then you give it a uh you give it two types. One is the type of the data structure you're protecting. The other is the type of the operational type over that value. So in this case um this is an like in left right unlike with a a reader writer lock for example you need to define what operations are possible to do on that data type so that they can be replayed later. Um so left right from a very base level it keeps two copies of your data structure a left and a right copy. uh and so the writer is always modifying one of them. The readers are always going to the other one. But that means that you all your rights have to be applied twice. Once to the left and once to the right map uh or data structure and so um you need to keep a log of all the rights you did so that after you've applied them to this, you now need to remember them so that you could apply them to this later. And so that's what this this operational type is. So it's generic over two things. Um, and when you create a new thing like this, you get back two handles, a write handle and a read handle. The read handle is clonable. So you can create as many as you want. Give each one to a reading thread, for example. The right handle is not clonable. Um, and so it is a thing that only you can only have one writer unless you put this in a mutex or something. Um, and so the right handle is where a lot of the logic lives for swapping between the maps, keeping track of the operational logs, uh, waiting until all the readers have moved from one map to the other so that it's safe to write to one. All of that logic lives in the in the right handle. As when they say here fix the data on the right handle drop, they're implying that when you drop a right handle, there's some kind of data race uh, presumably around the axis to the two maps. Um, let's see. that allows read handles to enter while the value is being dropped. The issue was observed by building with thread sanitizer in a test where one thread creates a right handle and shares a read handle factory for it with some other threads. A read handle factory is a a thing that can make new read handles but is not itself a read handle. Um the reason this can be useful is because the work that a writer has to do is proportional to the number of active read handles. uh read handle factory does not increase the performance penalty of a right handle. Um and so you might have a factory so that you can produce read handles if you need them but they are not themselves read handles. Uh T1 continuously creates and drops right handles sharing the factory accordingly while the other threads create new reles from the factory and attempt to enter. Uh so enter here is um if you look at read handle um on read handle you can call enter and that sort of means give me access to the underlying data structure. I want to do a read uh and you get back a read guard and the read guard is the thing that actually dreferences to the underlying data structure. The need the reason you would need to have this guard is the same the reason you need to have a a guard for something like a reader writer lock is because while you hold the guard uh the writer can't modify the map you're reading from. Um but the moment you release that guard you're sort of giving up your um uh your ability to have access. I don't want to say exclusive access because readers share it with each other but to have access to that and you will need to do more work to coordinate in order to enter again. Um, okay. Uh thread sanitizer here is a is a very common concurrency checking tool that essentially um you often run it in CI but you don't have to where it um it keeps track of all the memory accesses by all of the threads in a program and it checks that um none of them do atomic accesses in such a way that they could race like two threads uh concurrently writing to the same memory location without having done some kind um atomic for that right for example to ensure that they have not stepped on each other's toes without realizing um so Tzan reports a race where some threads manage to read a bool within V while T1 drops it uh co-authored with Claude surprisingly common these days um yeah so you'll see they posted this in April uh then they did a follow-up in May and I just haven't had a chance to look at it since because I've been moving and stuff. Uh this is sadly one of the realities of open source uh software as well. So let's see what's the change here. Okay. So this is in the um uh in the swap function. Yeah. Okay. So Oh, it's not in swap. It's in drop right so we have an impul drop from right handle for right handle um and what right handle will do is it will um uh take the inner and the reason this is a take is because I want to consume the the fields that are inside of right handle um whereas drop only gives you an exclusive reference to them uh and so if you actually looked at the definition of right handle which we can do over here. Um uh and then I will do let me uh see if I can move this so you can't see it and then I will mute myself so you can't hear me. and then let's try to pull again. Excellent. Um okay. So if we now look at left right here and we look at the right handle, you'll see the right handle um here. So if we go to the take inner Um, take inner is the thing that takes out the the backing data structure. Um, and the backing data structure here is if you have a right handle to a T, it basically gives you back the T so that it can be dropped. And this is a tricky operation, right? Because there are actually two T's, the left and the right. Um, and you need to only give it out of the take inner for like actual dropping. um once there are no readers in left or right because if there are any readers left in either of them then now um you're trying to drop the data structure while someone is reading it and so that's not okay. Um so if we go back to right handle you'll see that it holds um a W handle which is a non-null so a pointer to a T and it holds an R handle uh which is a read handle to a T and read handle inside is itself an atomic pointer to a T. Right? So there's two pointers to T's here. One is the right handle, one is the the read handle. Um, and so these are the ones that we need to get get the owned T out of because currently these are just raw pointers. So we need to get the owned T outs so that we can um let them actually be dropped. And this is where there seems to be a um a race of some kind. Yeah. So you'll see the the docs here too. Uh make sure that all the pending operations are applied and waits till all the read handles have departed. Um then it uses drop first to drop one of the copies of the data and then returns the other copy as a taken smart pointer. Ah so this actually I I misremembered this one. It it will actually drop one of the copies and then give you back the other one owned. It will not give you back both copies. Um right. So it keeps track of if you've already taken then it it refuses to give them to you again because if they've been taken you've given out an owned copy. you're not allowed to give out another one even if you technically still have the the raw pointer. Uh first ensure both copies are up to date. Yeah. So this is doing so publish is the thing that makes sure writes are applied to both sides and and swaps the atomic pointers and wait for readers to depart. Um and then we grab the read handle and we set it to null and then we wait for all the readers to depart. Yeah. So this is making it so that any read that happens will observe that the read handle um is now a null pointer rather than a pointer to a real data structure. Uh and then we do this um this wait call and wait is the one that will wait for every reader to have seen the last change to the atomic pointer. Right? Right? So we swap it to null and then we wait until all the readers have now observed a null. Um and then once we've done that now all the readers must have observed a null. Um and therefore uh they all know that the data structure is null and void. And so therefore it should now be fine to drop drop one and give back the other and take ownership of the other. Um, but this is where they claim that there's a bug. I'm sure this one is um last episode with a snapshot taken after the null swap above. Without this refresh, wait would use a snapshot taken at the end of the previous publish and would skip any reader whose epic was even at that point. Ah, okay. So, there's a bit of nuance here. So if we go to publish um publish's job is basically wait for so let me try to explain the algorithm top to bottom and then explain how it's actually implemented. Um so you have two maps left and right. Initially the writer writes to left and the readers read from right. Uh and there's an atomic pointer that points the readers to the right and away from the the one that's being written. The writer then has exclusive access to left. um data structure. So it keeps making modifications to that. Um and then when the writer wants to make a change, it makes a change to that that writable side and then it does an atomic pointer swap. So all all readers in the future will now go to here. Um and then it it um then it waits for so every reader keeps a sort of um atomic epic. It's a it's an atomic counter that's per reader and it waits until it's observed that every reader has ticked its counter at least once which implies that it's read the pointer at least once since the swap has happened. So they must now be reading the the new pointer value rather than the old one which means they can no longer be in the in the old map and this is what the read guard guards against. Um so you you swap it over and then you wait for everyone to move. uh and once everyone has moved now it's safe for you to use the the right hand side for writing because the left hand side is now uh for readers. The way this is actually implemented is slightly different which is that publish starts by waiting and then it does the updates and then the swap. So you'll notice that the wait isn't doesn't happen after the update, it happens before. Um the reason for this is a is a little bit subtle but basically um we do the swap and when we've done the swap we've applied the rights and when we do the swap reads will start immediately seeing the new values that we've written and so you still have this sort of consistency guarantee that when publish returns new readers will see the new value. However, there's no reason to really wait until the all the old readers move over as well before you return from publish. So, you might as well just overlap in time that waiting with the like the code that runs after publish returns. So you do the swap and then you uh read all of their epic counters uh and then you just return from publish and then the next time someone wants to publish which implies they want to apply some rights then you uh only at that point do you go and sort of wait until the readers have moved on because chances are in the intervening time between the last time you returned from publish and the next call to publish some time has passed. So readers have probably moved on. So this wait might actually be really fast. Um but this means that the the last thing that happens when you call publish is a swap. Um we can go look at this um down here. So we do a swap and then we read all the epic counters and then we return which means that after this point uh readers may still be in both maps, right? There's no guarantee that the the readers have have all all moved to one map. Um and so the question now if we go to take inner is so we try to ensure that all the readers have moved over and so yeah so the oplog here are like pending rights that we've not published yet. Um so if there are pending rights then we do a publish. So at at this point uh all rights are applied to at least one map or at least one T. Um uh readers are all in. So, we've waited for them all to move and then we did a publish, which means we did a write uh we did a a pointer swap. Uh and then we did a we did a wait, we did a pointer swap, we did an epic read, which is the readers uh may be in either t and so here after this publish all rights are in uh both t's i.e. there are no pending rights. Um, and so here we have the still have the same property that readers may still be in either t. There's also a world in which the oplog is empty the entire time. So we don't call publish at all like there there have been no changes in which case readers may still be in either t. So uh here readers may be in either T. Um epic is the last scene epic uh is the epic uh epics are synced with the end of the last publish. And then we swap to null and then we lock the epics. I see. So there's a world here where so we need to go look at what actually happens to um uh when new readers are created. So the the structure that keeps track of all these epic counters for the readers is an arc mutex of a slab. The slab is not super important here. Um but it's just an arc mutx. You can think of this sort of as a vector. Um, and inside of that is an arc to an atomic u size. Um, you need the double arcing here because this arc is an arc that's shared between one reader and the writer, right? Because they both need to have access to that reader's count um epic counter. Uh, and we need one of those for every reader. And then we need a mutex around that thing so that we can add more readers over time. That requires inserting into this this effectively this vec. Um and that needs to be shared between the read handle factories and the writer so that the read handle factories can make more readers. They need to have access to this mutx and so that's why the outer arc is here. Um and so if we go look at um factory you will see that uh cloning a factory is easy. You just clone both the arcs. Um the inner here is the atomic pointer to the t. Um this is the atomic pointer that we'll we'll swap. Uh and this is the um the epic counter arc mutx. Now when you create a handle you do this thing new with arc on read handle uh and that will obviously create a new epic counter and then it takes the lock and it inserts the epic counter that we got. And that's all you do. So you just you just add to this list. Um, and if we now go back uh to not here, back to here. The other thing we need to look at is what happens in publish uh in wait um if you have new readers that have come in. So you'll see that what weight will do you see we take the lock outside of weight and then what weight will do is it will um you know it keeps track of what the last epic it saw for every reader is um and it will resize that to um account for the current capacity of epics. So this one is is stored inside of the right handle is not behind a mutx. Uh this one is under the mutex that we're currently holding. Um, and so we extend this by nulls by by zeros to match the the capacity here. And then we loop through uh all of the epics and we check whether they're even. So let's ignore the even rule for now. It's not terribly important, but we basically check whether the epic counter that we have is the same as the one we read last, in which case they've not moved along or if it's different like here, then it must have seen the last swap because they've exited, they've dropped the read guard, which increments the counter. Uh, and so this is the case where you no longer need to check that counter. Um, does this mean that dropping the right handle could take arbitrarily long? Yes, exactly. It will wait. it will have to wait for as long as it takes for all the readers to depart. Um, and so the problem we run into here is if we go back here, uh, or at least what's pointed out in this is that what if a new reader was added, um, after one of these publishes, but before this and it has to be before this right because after this we've taken the lock so that there's nothing uh there's nothing they can enter. So if a new reader is added here, um then that reader will be in either map, which is fine. All readers are in either map. Here we'll swap to null. That reader will not have seen this yet. Then we lock the epic accounting structure and then we wait. Why is this weight insufficient? Uh this weight should wait for all readers including new ones to have seen the null. Why does it not? Oh, I think I know what the bug is. I think the problem here is actually this zero. Uh yeah, I think that's the problem. Let me try to explain why. So the rule is not actually that the readers increment their counter once for every read. they actually increment it twice for every read. They increment it when they um like the moment they read the moment they've read the pointer and they increment it again the moment they drop that guard the read guard. The reason they do it twice is so that if you have an idle reader like one that just sits on a thread that's doing nothing, you don't want that to block right handles because it's not holding a guard. it it hasn't read the pointer. It doesn't you don't need to block on it, but you need to observe the fact that it's idle. And you can do that by observing whether the counter is even. If it's even, that means that that reader is not holding a guard right now. Um, but you also still need to keep track of this like not equal because you could have a reader that's active, but it's active after having read the pointer a bunch of times. So, you still can you it's fine. You've it has moved on to the map you've swapped to. The problem here is if there are new readers added, we add them in with an epicounter of zero, which implies which is an even number, right? Mod 2 is zero. And so therefore, when we go through this loop, new epics uh sorry, new uh new read handles we will not wait for because they will hit this check. And so they will not block our weight returning. And then uh obviously they'll be filled in uh where they'll be filled in in um so in publish there's update and swap. So in update and swap at the end we read in all the epics. So after the the next publish after a uh a new epic counter is added then we add read in their last epic so it's no longer zero but before that so if we if we do a publish and a reader is created after that publish then its last epic will be zero which will be even which will be ignored for weight even though it might already be holding a guard. So I think the actual bug here is that this needs to be a one. This needs to be not even. Um the default right that's what that argument is called. Yeah that the uh defaulted value here must not be odd. Uh and we'll put in a ref to number 144. Uh otherwise new readers since the last um publish won't be waited for. And this this solves the problem because it reads all of the epics and then calls wait. But that shouldn't actually be necessary. Um it's fine for those epics to be stale. Um because actually is it fine for them to be stale? That's another interesting question because imagine that neither publish happens. So this is for sure a bug. But imagine that neither publish here happens. So there's some very old publish that last read the epics. Um and then we swap. Then this weight will wait for them to have observed some pointer swap. they'll they'll have observed at least some pointer swap since the last publish, but that could just be the pointer swap of the publish. It is not guaranteed that they've seen this pointer swap. Um, yeah. So we do actually also need to here read all the epic counters again so that we're when we wait we're we ensure that we actually wait for them to observe this swap not just the swap that happened in here for example. So this is actually correct. Um Interesting. Yeah, this description isn't quite right. I mean, it it gets at the problem, but it's not quite the problem. So, I think I want to write my own version of this one. Um, this is not entirely uncommon with PRs like this where um I could either try to guide them into what I want this to say or I can just write it myself. Um so actually what I will do here is this and then I will do uh minor left right gets stash um and then I will check out PR 144. Uh why? Why? What? Really? Okay. 53 2 one F826. Yes. Yes. Yes. Yes. Yes. That's fine. Excellent. Uh now can I check this out? Excellent. U. So now I'm on their branch. And so now I can go in here and say um refresh last after the null swap above. Um otherwise we would only be waiting for the readers to have observed some swap since the last publish but not necessarily our no swap. in particular. Uh they may simply have observed the swap at the last publish. Um and then this needs to be one again. Uh here actually it can be zero because we immediately overwrite it. Um but we we should keep it one in in um and then now so this is no longer true, right? This is where that comment should be. And this is uh well I guess this can stay here. And then this is uh uh and now block until every reader has moved on. Do the actual blocking loop till every reader has moved on. Yeah, I'm not a huge fan of um of uppercas and comments. I don't I don't know why. I don't know where I got that from. Um it would be better to actually have the test case here. So, I'm going to ask them to um to maybe add that in. Um, and then the other thing I'll do is I'll put in my changes here. And I'll actually leave this as zero and then I'll make the 0ero to one change separately. Um working on publish fix um and then I'll dash pump and then now uh maybe in Okay. And now we'll do this. We'll do a reset uh head to bring back that one little X change I did. Um and then here we'll do this and say uh newly added epics. Uh avoid ignore is skipping newly added epics in weight. Um a zero would be missed by uh would be considered even is is considered even. Uh and so would be skipped in the loop even though that read handle may have done um may have done rights. I may have done reads. There's actually another interesting question here of is one even the correct value because uh this means that if the reader does any value like assuming the new reader was added and then it's done a bunch of reads. Um then maybe it's correct to ignore them because or maybe it doesn't matter because they must have been created after the publish and so then the swap has already happened which means they must have observed the swap. up and so therefore it's fine for us to not wait for them. Yeah. So I think actually this change isn't relevant. I think it's it's okay to ignore them, but the reason is a little subtle. So maybe it's actually fine for it to stay zero as the default value. Okay, I think I think I believe myself again. So that means these can stay zero. Uh although I do want to leave a sort of note to myself here about that fact. So we're going to do check outp. We're going to check out this and then we're going to go here and note that the folder value here is odd. So, new readers since the last publish uh since they were created after the after the swap. Anyway, um however, since they were created after the swap anyway, uh they must have observed that swap and so it's fine not to. And so we don't need to wait for them. So just a clarifying comment. I'll leave these two clarifying comments in there as well. Uh clarify why zero is okay. Um and then I'll push [sighs] uh yes I do want to I don't understand why I'm not allowed to do that. I thought it would sync this to that remote as uh what I've definitely done this before where I could just push to it. Does it say I'm not allowed to push to this because if so that's pretty annoying. Um it also needs to merge with main. So we'll merge that in there. Um but oh did I mean even? Did I misspell? Oh the default value here is even. You are completely right. Oops. Um fine add uh pretty I was pretty sure that GHPR checkout would set up the remote, but I guess not. Okay. So, then we'll have to do this the old way. Uh, which is I need to do uh this is really annoying. Uh, get remote add um this and then get atgithub.com that slashleft right.get Get get fetch uh get to there Freddy. Aha. I think that's why I'm not allowed to push to this PR because I think they've not granted uh the ability to for committers to push to their branches. which I guess they've turned off which is annoying because it means I basically can't take their change. Um well then we will do it uh in our own branch instead. Um so I will then go ahead to main. I'll how do I want to do this actually? I think I want to um candle drop rice and then I'll reset that to this and then I'll commit um race condition in right handle drop um closest 144 four. Um yeah, I guess I'll put in the description here that um uh previously the weight and take inner I guess also affects um right handle take Uh, previously the weight intake inner would um could return even if uh could return if some new uh read handles had only observed the last publish but not the swap to null. uh fix this by uh reading the epics again after the null swap. Um and then way to observe them take over replaces 144 since uh I could not push to their PR branch. want to make some changes to the comments. So, I'll push that up like so. Um, wait. Oh, that's not what that's not what I wanted to do. Uh, that's not at all what I wanted to do. Um, I did a stupid because now I think I just undid all those changes. Yeah. Um, what I actually want to do is reset um I want to reset hard to Freddy and then I want to reset the branch to main. Now I have the right thing and then I want to get commit A like so. So this should now be right. Yes. force release. Okay. Uh and then I will go over here and I'll say sorry for the delay. The middle of a house move. So time has been typed. um wanted to make some smaller modifications to the comments here, but it seems I can't push to your branch. So, I'm landing this in definitely a bug here. Uh, one request though. Could you um open a PR where you add the um the TSAN test case that we catch regressions on this in the future? Closing comment. Now let's see what's this unhappy about. It's unhappy about codev is sometimes just unhappy. So that's fine. Um I can't approve my own PRs, can I? No, I cannot. Okay, that's fine. So this now looks good. This just takes forever, which is fine. So, we are going to It is me who've trained AI to use M dashes is true. I love M dashes. Um I've used them a lot way before AI. Cool. We'll merge this. Um and then I'll keep it I'll keep it open and in tab because we uh if I do another release of left right to to include this change, then um I'll want to post the version number that I release it with in here. Uh but I want to see these other ones first whether that's something we need to change. Uh can we wrap wait and publish under condition for first publish? Uh on first publish call there are no readers on the right handle. So we can just wrap that in not on first. [snorts] I see you don't need to wait because you know that there are no readers in the right handle. So you can update it straight away. I think that's true. First there are no readers can be on the right handles and no need to wait. Yeah, I think that's true. The only thing we need to be careful about is whether there are any side effects to wait. Um, but I don't think there are like it doesn't read the epics or anything. So, uh, would it make sense to drop a co-authored by them on the commit? Probably. I mean, I reference their MR, right, or PR? Um, and so the the the the sort of chain of custody here is clear, but but it would have been good. I agree. Um, I simply forgot that that is a a thing you can do in git. Um, I think this is correct. So, I'm fine taking this one. Um, uh, on first publish, no readers are are in the right handle, so no need to wait. approve. Just want that one change to make the comments more in my style. And then I the update with a rebase. It shouldn't be a problem. I think it's just the CI that's changed. [snorts] And so this is another change so we can get into the same release. Uh so I'll go back to main here. And so once CI is happy there, this is done. Um relax re-entrant null pointer assumption. The re-entrant read handle enter seems to have an unreachable assumption that's a bit too strong. Right handle drop can indeed swap the pointer to null while the outer guard keeps the reader epic active. For instance, right? So let's first look at the read. RS uh so we have an unreachable here. So, self. Uh, oh yeah, this is a this is to check that if you tr try to enter the same read handle multiple times, then the the second one is a no up because you you don't want to count. Let's look here. Um yeah, so uh imagine you like just in one thread you call enter and then you immediately call enter again on the same read handle. The second one should not increment the counter the epic counter a second time because then it would appear to be even. So the second one needs to do nothing. So we have to we have to have this guard this enter guard to make sure that if we are entering twice um then the the second one is sort of a no-op we just give out another guard. Um but the you'll see here the read handle state that goes in the guard uh keeps track of which enter it is so that you know when to actually um escape. So you'll see oops um you'll see it keeps a a reference to the cell of the read handle so that they can keep track of the last one that's dropped and only the last one that dropped sets it back to to um to even. So you'll see this just gives you another read guard. So it it still works. It's just that that one doesn't do anything. But there's a guard here that says if the read handle is null um then we don't give out the read guard. So the question is why is this marked as unreachable? If the pointer is null, no readguard should have been issued. Ah so this is incorrect because and this relates to the thing we just fixed um which is during drop the right handle is going to set the um the the the pointer the atomic pointer to null to indicate you can't access this anymore. Um, so it's not the case that it's just during setup that it's null. It can also be null during tear down. And so then we should indeed uh return none here. Um, and so I'm guessing that's what they're going to show, right? So left, right, new, enter. Uh, in another thread you drop the writer and then you wait until it's actually dropped. And when it is the you should expect enter to return none. Um but instead it it panics. Yeah. And I think that's totally right. It should just return none. Um it would be good to have this as a test though. Um let's see if we go back here. Uh PR check out. Let's see if I can push to this one. Uh yeah. So see I've been inconsistent here about the uh capitalization. Fix that up. Uh the right dropped. Uh right handle may have been dropped. Uh, in fact, must have been dropped, right? I don't know. Must have been dropped. Um, and the epic would be active anyway. So the other readguard that we issued in the first enter could still be some right because it it's still active. It's still sort of holding up the um uh the right handle drop must have been dropped. Um so uh um so even though the first enter may have returned some all we can do now is return since the um point t has vanished. still active somewhere since our first enter must be preventing it from being claimed yet. Um uh slight clarification or More words. Doesn't matter. It's going to be squashed anyway. Can I push to this one? Can I really not head? I don't understand why I can't am I like missing something in my setup of this? Let's see. Get uh get remote add left. Right. Get fetch this one. Get push this. Yeah, but I don't understand what the It's true that the PR creator can decide if maintainers have access, but I feel like normally people check that box or at least I think it's checked by default. This is two PRs in a row where people don't have that. Um uh that's annoying. Um why why are these checks not running? Ah approve workflow to run. Um well I guess we will have to do the same thing here then which is uh check out main and then get pull and then uh make uh re-entrant enter during drop uh and then we will get reset hard for this guy and that we will get reset domain. H right this also needs to merge main. So it gets that change and then we will get reset hard to the fix and then get reset to main. So now we have this uh and now we can actually do um now we can also add this as a test in read. Where's my where are my tests here? Actually we could even do this with Loom. Um, but I don't think we need to. I think this is actually sufficiently deterministic. It is a little bit annoying actually to write tests for this. I don't know that I have been very good about having a easy to extend test suite here. Um, part of what's annoying about it is you would need to have a a data structure that implements the traits required for left, right, which I believe this one does, but it doesn't have that trait. So let's go ahead and simply try this. Um re-entrant enter during drop. So, what's the uh DQ and OP? DQ and OP. And we'll import that. Uh I don't think this needs to happen in its own thread. I think you can just drop it here. Uh and then we can assert this is none. uh value that's fine. This can be derived debug. No. Oh, this is like a test data structure I set up just for this test. And I guess now Oh, and then debug partial eek and eek. And this can also be partially eek. You happy now? Okay. Uh, minor left. I don't know why that was even there because I don't think it needed to be. Uh, read Guard doesn't implement partial leak. There's no real reason why it can't. I think maybe that's a thing we should do at the Whoa. Something isn't happy with me. Uh, why can't readguard implement partial leak? I think it should. Uh, and in fact, I think this is something we should add. Uh, but we should not add that in this PR. So maybe this is another good modification for us to make just not right now. Uh so then we'll just go with the original assert is none. This can go away. Oh, I know why it's happening in a different thread. It's happening in a different thread because this will block forever because this guard exists. That's why uh so this no um so this actually needs to be is none and then this needs to be uh what they have they had like drop writer this which is fine I don't know why they assign it to a I think this can just be that uh and now this is going to complain because I happen to use an arc here. H I'm using an RC so this isn't thread safe. Do I really want to make this whole thing not thread safe? I don't think so. I actually think I want to create a new little test suite for this. Um tests uh simple. [clears throat] Let's call it simple. Um so that means this is going to go away from there. Uh these I'm going to just undo so this stays as simple as it or it stays the way it was. um like so. And then here we're going to make value simply be an i32. It's not going to have this value registry. The value stuff is um a thing I added to be make it easier to write tests to check exactly what operations are applied. But you don't actually need those. Um so we can actually simplify this quite a lot. We just do this. So, uh, don't need the drop. Don't need the value registry. That can all go away. Um, don't need these. In fact, maybe I should just have it be counter. Fact I already have a counter type I think that we can use here. Yeah, we can just take this one. Uh have so instead of all of this stuff, let's just use the one that's already the example for left right. So, uh, we don't need all the docs here. And I think this is also a defaulted value. No, apparently not. Okay. Uh, and now we can write our little tests. Uh, which is a lot of this stuff down here can go away. We don't need all the value stuff anymore and the DQ and everything. Uh 24. Okay. And then we don't need these helpers. And we can just do this. This will be the actual test case. This should now be thread safe and we don't need the handles. [snorts] Uh, great. Yeah, re-entrance during drop. And if I now try to go back and undo this fix uh and here set this to unreachable that failed. Great. Uh and then I think actually let's move test simple to test counterrs. Uh okay some spammer in the chat. Let's get rid of that person. Goodbye. Uh, what do we wanna? Let's just do that. Sure. Report. Goodbye. Hide user. Goodbye. Um, great. So now we have an actual test that catches this. So now we can add test counter. uh and we can check out testdQ so that we don't make any changes to that uh and then here we can now commit this with uh I'll retain this and then I'll also do the um I'll also keep here the patch what have they changed that this used to be a thing you could do. Ah I need to do this I think and then patch because then I can do this uh and then I can write what was it co-authored by um and then I'll say here um it's possible for read handle will enter to observe null even after an initial initial successful enter. Uh specifically when right handle drop is running concurrently. Um so C handle right on drop is running concurrently. Um so the unreachable was a little too strong. Uh and then I will also do actually um replaces number 50 since I wanted to add a test. and modify the comments. But the PR did not allow maintainer pushes push you origin re-entrant. So, uh, I think this Well, that's not happy. Why is that not happy? That made all the tests fail. This is the the one where we skipped the called to wait on first publish. Why? [snorts] Why indeed? Let's go down and look at one of these failures. Whoa. panicked that read 59. What? Why would it fail there? This is what I mean by side effects of uh of weight. So, what else does weight do? Resizes last epics, walks through epics, loads from them, but that's all it does. So why that's fascinating. Wait, it fails earlier, right? 449 length is zero but the index is zero. Oh, I see. um weight is the only thing that resizes last epics. And so now that we no longer call wait in the first case, if you have a test where you only call publish exactly once, what you'll end up doing is you'll never resize last epics, but you will hit this loop which assumes that last epics has every index that's in epics. And so this just needs a resize. This just needs um and when when this panics that causes the other one. So if we now find uh fn weight and we take this from there we do this uh like so [snorts] that needs to be on the other branch though which is uh oh no oh no uh am I going to be allowed to push to this one? What do we think? So if I now do get diff get stash uh PR checkout 149. Okay. Get slash pop uh publish may not uh wait may not have publish. Can I push now? This one I can push too. I don't know, man. Okay. Uh, approve. This one fails just because of coverage. So, that's fine. So, this one we can merge. Confirm and merge. Great. Uh so then I will go here and say in this guy uh with uh 154. So close that one. Excellent. And now this should start to go green. Yep, this is green. Apart from the coverage, we can update this with a rebase to get sure we get the CI for the latest one. Uh, and then we can start to prep a release here. So, this will be 11.8. We didn't make any breaking changes. Uh we'll do a cargo update. Is there anything we're behind on? No, cuz we have basically no dependencies. Although, what is the latest Loom? Is it still 07? Yeah. Okay, great. Then I need to refresh in order to see the approve button, which I don't know why GitHub hasn't fixed that issue yet. This has been a feature for a while. Uh, and then once this merges, we should be good to go. And then we'll open a ah, and then I also need to undo this because otherwise I break the check with the older rust version. And now we twiddle our thumbs until all of those pass, which should be pretty fast. I really should figure out why coverage doesn't work. It might be It might be something stupid. Is there a I wonder what version of GitHub workflows coverage? Oh, why is this on the old style coverage because this has moved to seven now I think. Yeah. So that needs to move to seven. So we can actually fix that at the same time. So uh we'll do here this will move to seven with this commit. All right. Now just coverage which is fine. And so we squash and merge like so. Excellent. And now we pull to get in that change. We cargo update. We undo this. Ah, great. And now we do release 011. What did I bump it to? Did I bump it to seven or eight? Eight. toml. Yes. Great. Um 0.11.8 release 11 8 so that we can check that CI is actually happy. Let us see. and now hopefully coverage should be green too. And then we'll tag and upload. Why not use something like cargo release? H I could the the main reason I there are a couple of reasons I don't I I don't like CI having the power to publish packages. Um even though I know there's been a lot of work on making it secure, I just I don't like it. Um the other is I kind of like the ritual of it of doing a release. Um it lets me I don't know. This is something where I'm like yes, I've I've gone through the steps and agreed this should be a release. Uh cool. So go in and squash that. Boom. And then we'll go back to main get pull. We'll tag uh and then we will cargo publish and get push the tags. [snorts] We'll go back here and we'll go released in 0.11.8. Eight. This one. This one. And this one. Done. That can go away. That can go away. That can go away. And this is done. We can delete the branch. Uh, something I learned recently. You can do git push- origin head to automatically push the remote with the same branch name you're using locally. Yeah, I know. It's neat. Excellent. So, we did left right. Good job, us is only took us an hour and a half. Um, so what do we want to do now? The sponsor of today's video and the first ever sponsor of the channel is Hudson River Trading. HRT is a quantitative trading firm using the latest advancements in machine learning, high performance computing, and systems engineering to make trades in over 200 global markets. They pride themselves in having a deep engineering culture and a relentless focus on performance. HRT is primarily a C++ and Python shop, at least for now. But the kinds of things that draw people to Rust in the first place are exactly what they're facing daily. Delving deep into the lowest levels of their hardware and software stack, including instruction set optimization and compiler tuning, all whilst meeting strict correctness requirements. They're hiring in offices across the globe right now. And if you're interested in joining, check out the link in the video description to learn more. What do we want to do next? Um, let's do HDR histogram because I see there's a bunch of things on there. And then maybe we do inferno after that. So HDR histogram is this one, this one, this one, this one, and this one. And are those the only ones? Five of them. Five. Great. Uh so what is HR histogram? So for those who are not aware, HR histogram is not a data structure that was invented by me. Um it was actually originally written for Java by Jill Teen. uh and then there's been ports to a bunch of different languages and I happen to do the rust port. It is a way to keep a um a histogram. So think like uh you know a bin diagram where the x-axis is some um unit you care about like let's say the number of milliseconds it takes to process a request and the y- axis is the number of times you've observed a given uh amount of time. Right? So you can see things like oh it um yeah 100 requests took more than took 5 milliseconds uh three requests took uh 120 milliseconds and so on and you sort of bin those um and normally with a histogram it's a little annoying because you need to set your lower and upper bound and then you need to have a like you need to fix your bin size. So let's say you wanted to capture everything from requests taking 1 millisecond to a second or let's take the easy case from 1 millisecond to 1,000 milliseconds which I guess is to a second u but 1 millonds to 100,000 milliseconds then you need to choose what resolution you want to have. So um you could say you could put it into five bins. So you would say any request that takes between 0 and 200 milliseconds I'm going to count here. anything between 200 and 400 I would count here and so on. So you would kept five bins. Um and so this way you're only really keeping five integers but it also limits the resolution that you get for the histogram. So you can't tell out of the requests that end up in the 0 to 200 bucket. How many took one millisecond and how many took 200 because they're all grouped in the same way and that's frustrating. So you might want finer grained buckets but the more fine grain they are the more buckets you need. So if you wanted like one if you wanted millisecond precision then you need a thousand buckets. Um if you also wanted to capture like below a microscond maybe more seconds if you had really slow requests then now you might need 10,000 buckets or 100,000 buckets and all of that needs to be stored in memory. So it becomes pretty intensive. Um HDR histogram is a high dynamic range histogram. So instead of the buckets being linear in size like all the buckets are the same size um the buckets are roughly speaking um logarithmically spaced. So if you have um like let's say you wanted to span from you know one microscond to 10 seconds instead of having the what would that be like um 10 million bins um you would instead have one bin that's let's say 1 to 10 microsconds one that's 10 to 100 microsconds one that's 100 to a thousand microsconds one that's 1 millisecond to 10 milliseconds and so on And those buckets are not the same size, right? The one that goes from um 0 to 10 microsconds is only 10 microsconds long. The one from 10 to 100 is 90 microconds long. And so they're not evenly spaced. And the HDR histogram basically has a a an algorithm for determining that logarithmic spacing such that it's still pretty fast to look up which bin a given latency you want to record goes into. And it doesn't just have to be latencies. It can be any anything on the the x-axis. But the intent is that you now get um finer grain granularity across a wider spectrum of data um without having to store something that's linear in the space you're trying to capture in. Um so super neat data structure uh and the Rust version I think is pretty neat. Um and so let's see. So, we have one unound buffer initialization. That should be pretty easy to fix uh from two weeks ago. Uh Marshall Pierce is one of the other maintainers of that crate. Seems Marshall is on top of this one. Has a PR that fixes this. [laughter] The unsafe usage in V2 serializer was only worth maybe a 5% speed up assuming it was correct which it was not. Past me did not worry enough about unsafe. Uh let's go ahead and look and see what that fixes. Resize a lot of dummy bytes. Yeah, because resize um when you when you call resize it resizes a vector. So it allocates more capacity for it and it has to go write zeros or whatever you pass as the the value here into each of those slots and that writing might actually be the majority of the time because the resize is actually pretty fast but it leaves uninitialized memory in that space. Um, so this the unsafe function set length that just lets you set the length of the vector to be equal to something that's greater than its current length but less than or equal to its capacity and just leave the stuff in between uninitialized under the assumption that you will initialize it by writing stuff into it. Um, and so you don't necessarily want the the zeros to be written because you're already going to write some other value. you're not allowed to leave those values just uninitialized because that's undefined behavior in Rust to have values that are readable by safe code that don't have an initialized value. And so it looks like the original audit here was um yeah and code counts only writes up to total length which might be less than the max size here meaning the trailing capacity might be permanently uninitialized. Um and so therefore serializing Instagram triggers this this uh and this was seemingly found by me. Uh cool. So the fix here in this case just be like that optimization is not worth it. Let's just write the zeros. Um although it is interesting because why why are we even extending it to max size? Like why not just extend it to total len instead? And here's another unsafe forget unchecked. This is probably trying to optimize the bounce check, but the bounce check should be gone because of this assertion anyway. So, I don't think that should be a problem. And this is the same. And then forbid unsafe. Let's get rid of the annotations. Um, okay. Okay, I mean this seems fine, but I am confused about why we even do this and why this is in parentheses. Uh it all he he also said that there was a failing coverage rate limitly reached like the last release of HDR histogram was three years ago which is not because it's unmaintained it's because it's just hasn't really needed updates Um, I think I need to update the coverage settings. That's fine. But um, I'm confused why this shouldn't be total shouldn't simply be total len. I want to extend the slice all the way to max size when we're only ever writing len into it. Oh, I think I know why. It's because we don't know the total len until we've done the encoding. And the encoding needs to write into a buffer that it needs a a safe allocation into. That's why so you don't you have no way to know total len here because you compute it based on counclen and counsel len is only computable after you've given it a slice to write into. Um so that's why that's this seems fine to me. Um don't worry too much about the coverage. I think that's a repowide thing I need to fix. Proof. Cool. Squash and merge. Confirm. Okay. So, that fixes this one. So, we'll have to do a release. That's easy enough. This one. um a feature that adds single pass value at percentiles value at quantiles batch API. This is definitely this this looks like something Claude wrote, but I could be wrong. Uh quantiles. Okay. So, um there's a there's an API on on HDR histograms that lets you ask um what is the 95 95th percentile value for example. So the 95th percentile value would be um the value that 95% of your recorded samples are below. Um so for example the median is the 50th percentile. Um so 50% of your values are below the median. That's what median means. But that way you can also compute things like 95th percentile, 99th percentile, 99.9th percentile and so on. And currently the API for this is there's a uh there's a method called value at percentile where you give in a percentile and it gives you back the value there. The way that works is it basically walks all of the bins accumulating the count and then figures out where to stop based on the total count. So think of something like you know the histogram keeps track of I have a thousand values in my map or in my in my structure. Um and so it will start at the lowest value the lowest bin and it will add however many elements are in that bin to a counter. If that counter at the end of the bin is now uh more than or equal to the percentile that was the the if the count that we have so far divided by the total count is more than or equal to the percentile that was requested then you return that the bin the edge value of that bin. Otherwise you go to the next bin you include that in the count and now you check how many percentile you up to now. You keep walking up there until you've reached a point where now you've gone over the percentile they asked for and now you know that's the value at which that percentile was hit. Uh and the observation here is like what if you have multiple percentiles like you want to get the 50th, the 75th, the 90th, the 95th, the 99th each time you're starting the counting from the first bin. So rather than do that, what you have a thing where you can pass in multiple percentiles, get all the percentiles out and you can do it all in a single pass. So it's it's a it's a really good insight. um get the values at several quantiles in a single pass over the histogram. And they presumably also have a percentile. Uh yep. So the difference between a quantile and a percentile is that the um the percentiles are you pass in the value 95 and that implies 95%. Uh the quantile is you passed in um 0.95, right? Because 95 divided by 100. So so values at percentiles is just slightly more easier to understand for humans, but it's it's the same thing. Um it's a little sad that it has to be a vector, though. In fact, I wonder whether the interface to this should be an iterator. Um because currently we need to collect into this vec and in fact any caller that wants to use this data structure would need to have all of the quantiles or percentiles they wanted in something that's contiguous in memory in order to call into this. Um which isn't really necessary, right? The only thing that's necessary is that they are in sorted order. So imagine they have it for example in a B tree set or something then they would need to collect it into a ve and then pass it to this API. If we took an iterator that would be um that would probably be better and then we would just have to enforce that they are in sorted order and that's the part that's maybe annoying. Um let's see. So there's returns of vec. Uh can you return an iterator over the buckets with a cumulative quantile? Um, so in theory we could write this as a generator except that Rust doesn't have generators yet is the problem because then you could for each thing in the iterator you got in you produce an a value in the iterator but then you're basically implementing iterator um over another iterator and we we we could totally have that be the interface but then it wouldn't be a function in this way. uh if it is to be a function, it basically needs to be a generator function, which we don't have in in Rust today. Um but that would be a really neat way to do this. In the absence of that, the return value here will end up being a vector. The question is whether we should require that the input is a vector. Uh but let's first check that it's correct and then debate the interface here. So returns a back 64 with one entry per input quantile in the same order as quantiles. Input order is preserved even if quantiles is unsorted or contains duplicates. Each entries exactly what valued quantile would return for that quantile. But the counts array scanned only once regardless of how many quintiles are requested which is faster than n separate calls for n greater than one. So this is actually something we could pretty easily write a quick check for that these two are equivalent edge behavior matches valued quantile. Yeah. See so it um basically what it does it reads in all the quantiles into a vector and then it sorts that vector down here. In fact, it does this. It has multi it has it creates three different vectors which feels very unnecessary given that this um so HR histogram tries to be pretty fast. Um and doing three allocations here feels like it shouldn't be necessary. Per quantile target cumulative count. Yes. We basically turn the quantiles into counts by multiplying them by the the total count of elements we have. Uh we cap at one. Uh we round up and we ensure we count at least one element. This could just be a clamp. And then we resolve them in ascending order. So one scan is satisfies all of them. I think this these are integers. So this could just be a sort. I don't know why they're using sort by. This reads very weird. Why is this n comparison after the allocation? Oh, because it returns a vector. Okay. And then we hoist the next target into a local so the hot loop stays tight. Per crossing bookkeeping runs only when a threshold is actually reached. total to current index. So this is how many have we counted past and then it keeps track of what the next target count is. So that's the next thing in the order. We loop over the counts. Yeah. So the counts here are the the bins. track the bin. If it's above the next target, then that value is what we should return there. And then we need to write out Yeah, we need to turn that value into the the actually boundaries of that bin and then we move on to the next position in the quantiles array. Uh if we hit all of them we can return if total to less the next target. H this is written very strangely. Um I also I don't think this actually needs to be a separate vector. I think you can just compute the target count directly here when you update it. Like I totally buy that this is useful. I don't buy that this implementation is a good one. Um it's also [sighs] this is one of the things that I think is is difficult with um difficult with LLMs is like this code to me reads like it's LM code. Um and you can see it in like some things as like referring to things as rules. uh or like the m dash in the comment here is like pretty uncommon. Even I don't do that and I love m dashes. Uh and like this kind of articulation. Um, and the reason it matters here is not because I wouldn't accept something coming from an LLM, but it makes it difficult for me to figure out whether I should tell the author here what changes I would like to see or whether I should just do it myself. Um I see. And then they have Do we have um quick check in here? Oh, wait. No, that's the wrong project. Uh, it should be Oh, yeah. We ported. Did we write dare histogram histogram on stream? I guess maybe we did. That's fun. I've I've forgotten all the things we've done on stream, which is kind of funny. Uh that's that's a shame that we don't have prop tests in here because this feels like a pretty good candidate. Um batch and it's actually yeah uh batch value must return exactly what the singular value would have done for every edge unsorted plus duplicate inputs an empty histogram. So we create a histogram. We record a bunch of I guess semi- random values like is this is a the half mercen twister random number generator. Why not just use a random number generator? This is another example of something I'm like who writes this? Like what human writes this? [sighs] Um age value quantiles to the length is the same and check that for each one it's the same as what value of quantile would have given you. Same thing for percentile. Yeah. Okay. Uh test here is fine, I suppose, but I'm tempted to try to write a better version of this. Uh so let's go ahead and do PR checkout. The other thing that's missing here is actually value at quantile. Uh in fact if you are trying to Compute multiple percentiles. Prefer value at percentiles. Multiple There we go. Um, all right. In fact, let's make these nope. value at percentiles value at quantiles I can't spell Um, all right. Let's see what we can do about this. So, I think I want to change the contract here a little. Um, so we could make this return. We could take an iterator and return an iterator and say something like um we stop iterating if no longer in order or if we hit a duplicate. I don't know if that's a nicer interface. I think it is. Uh the other thing I want to check is so rust has a from fn iterator from fn which takes ah an fn mute. I think we might be able to write this as an FN mute [snorts] I think. So let's let's see what happens. So if we make this imple iterator in fact imple into iterator uh item is f64 and then we'll return something that's imple iterator item is U64 and we'll do the same for value at percentiles. And then this can now be percentiles into it map like so. And then here um returns an iterator per input quantile in the same order as quantiles uh but stops early. Um if a quantile is not strictly greater than the previous I uh provide quantiles in ascending order without duplicates. And then we'll put the same down here for uh like so. Okay. And now what we want is we can get rid of this. Uh we can say that target is going to be a closure that moves in total count. Uh you give in a Q and it gives you back Uh Q dot clamp between zero and one. The clamp allows the edge values I think right accuse NF64. Yep. So we clamp it from 0 to one, multiply it by total count, take the ceiling, uh, and then we clamp and we take that whole thing and we do max of that and one. Uh, And so this is normal uh not normalizes but clamps to zero to 1.0 uh minimum count of one. We don't need to do the sorting because we assume that from the input. Yeah, we could do the same as binary search and just say that the values might the returned stuff might just make no sense if it's not in order. That is maybe a better interface. Let's see how it feels when we write it. Um this whole thing we can go away. Uh we do need to keep track of the total to current index. We don't need to keep track of the position. Um, but we do need to keep track of uh so quantiles is going to be quantiles into map target count of Q. It's going to be targets. And then we are going to do um [sighs] mute uh counter. This is where it would be a lot easier if we had um it would be a lot easier if we had a generator functions here because we're basically going to sort of semi-handroll the generator function. But what I want is an iterator over self counts. Uh counts. Uh and I think it needs to be enumerated because I think I need the index as well actually. Why? Why? Oh yeah, because you need it for value four. Great. And then now we do uh stood itter from fn to return an iterator here. Um and it is going to move all these things in there. Um and then it's going to be mutating these and that's sort of going to be the state of the closure. Um it's going to mutate them as each time it runs. uh which is why it's going to be an FN mute, but that's fine because from FN allows it to be an FN mute. Um and so what do we want from this? It needs to return an option, which is fine. It returns none when there's no longer anything to return. Uh so then we say let um next target is target.next with a question mark. So if there's no next target, there's nothing to search for. And so we return um and then we will then loop here. This is going to continue the loop I and uh count is counts. Next. So we will keep taking from this iterate. So we have this iterator. We're trying to iterate over all the bins, right? I'm going to store that iterator here outside the closure so that it gets shared between each time the closure is invoked. And this will just continue that iterator from wherever it left off last. Um as we'll take out the i and the count. Uh this remains right. Uh that is plus equals that count. Um and then this remains. So if that is greater than or equal to the next target then uh of quantiles. Ah, so we actually need to keep um actually we can just do we can do this. So this will actually be quantiles. So next quantile is quantiles.next next. And then we don't even need that to be a function because we can just call it here and say next target is here. Next quantile, right? Um and then now here let's make this a comment. so that we actually get formatting here because this is getting annoying. Uh, and it's going to be upset that I don't return anything. So, let's just say none for now just so the errors go away. Um, why does it ah the total count? So, you see how we get a an error here. It might be hard to read. Let me run it here. uh it says hidden type for imple iterator captures lifetime that does not appear in it its bounds um and in particular I believe the problem here is this closure that we return is not allowed at least here we don't say anything about it borrowing from self but it will borrow from self right because the iterator here is borrowing um from selfc counts here and so we need to make it clear that this is actually borrowing from self um there's a A couple of ways to do that. One of them is, and this is the one that is in the newest Rust version, is to do um use. So we'll do um tick a tick a use tick a and so this is one way to do it. Um and that will be right, but it will only work with versions of Rust that support the use keyword. So, HDR histogram tries to support older versions of Rust, but this will make it no longer do that, which is a little sad. Um, [clears throat] uh, yeah, let's let's see if we want to keep that or not. The other thing is use needs when you use use, you need to explicitly mention all the type parameters that you use, which includes where's my uh, this is this file is way too long. The HDR histogram type is also generic over T. Um, and so we're going to have to say that uh this also uses T. What else is it complaining about? Uh, oh, and I also captured the iterator. Fine. This will be the iterator uh where I is this because I need to say that this also captures I and T and now it's happy. So right. So now we can finally in peace here go if we've passed the target then we can do this bit which is we get the value at that index that we just passed. Why is this a loop? This is a loop because oh this is handle duplicates correctly. So that's why this is a loop because if you have duplicates um this wants you to keep returning the same value. And so I suppose we could do the same thing but I don't I think we just want to ignore duplicates. I think we here want to say yeah. So this is where uh so we would need to keep previous quantile is none and then here we would need to say uh previous quantile is equal to some next quantile. Uh and then here we say if next quantile is sum and the previous quantile uh and the previous quantile is greater than or equal to the next quantile then that means they're not in sorted order or there are duplicates. then return none. Uh we could alternatively just peak the iterator. That is true. So we can make this a peakable iterator and then just look forward. It's effectively the same thing. Um it would mean we don't need to store this, I suppose. So we could say here peakable uh and then say if uh quantiles do peak but it's kind of annoying because you still want to return the value for this quantile. You only really want to error on the next one. Uh and so you would need to peak back which isn't a thing. So I don't think because I don't think we want to return none if the next one is invalid. I I guess we would do it um here right when you're about to return. So if quantiles peak is sum and next next quantile is less than this can just I don't know why these have to be next these can just be called that uh if next is next that is less than or equal to quantile then no it's the same problem right we still want to return sum for the one that was valid so I don't think this works I don't think it can be a peakable I think it actually needs to be this but I do want to make this just read quantile And this uh to read just target um duplicate or not in sorted order. So stop iterating. Um right. So now that we have that we need to do this bit. This is something that comes from um from the the highest value thing we already have. Um so the sorry not the highest value one the value at quantile that we already have is this rule that if the quantile is zero we use the lowest equivalent of the bin. So the low value of the bin um rather than the high value of the bin, right? Because every bin has a left edge and a right edge. Um so we do let result is um if quantile is zero then this um and then we here return some result. Uh, and if we get to the end, if we get to the end and we still have a quantile, then what do we do? I think this one just returns the value. But I'm curious to see what the um what the original one does. So if we go to value at quantile see how similar our code now looks to this one. Um but otherwise it returns zero. Why does it otherwise return zero? I mean I I guess we will mirror that then. So this will be some zero match uh value at quantile. Let's see how that does. Hidden type. Oh, this is for the percentile version. It needs to do this. It needs to look the same. So it needs to have the same here. Percentile percentiles. Oh, percentiles. And now it doesn't need to have a must use anymore. And ah I suppose this is yeah this can be this in byref. So byref is a thing you can do on iterators to say in so so normally when you do for something in and then you give it an iterator the four consumes the iterator whereas this one saying don't consume the iterator instead consume the iterator by reference um which I think is equivalent to us doing this um yeah that way we don't need it to be a while and it is also unhappy about something else. Oh, a lot of ellighted lifetimes. That is true. I should arguably fix that. Um, but these are all in pre-existing code and now it complains because wait, why does it need a compiler? Oh, cargo update. It's probably very old lock file. Let's see this. Uh, I'd write it just with while current total is less than the current total required for this quantile, then advanced quantiles and return after the loop. Duplicates would work automatically. Yeah, that's true. So, we could sort of flip this on its head. So instead of iterating through these, you do the you turn this into a while a while less than and then you only inc you increment this after realizing it's less than. Um, is that nicer? I don't know whether that's nicer. Oh, why is it unhappy? Oh, right. These test data access uh is empty won't work because these are now iterators. So, uh the singular would in input order with the same handling of corner cases. RNG. Don't I already have rand in there? I already have rand in there. So, just rand random mod this plus one, right? Like why why make this? There was even already rand there. Great. Um and now this is no longer going to be out of order. 1.5 minus 0.5 just to see what happens. Um, no, cuz minus 0.5 is going to be clamped to this one. So, that won't work. So, we're going to do this uh error in fact into iter because it's an array. odd copy. Uh and in fact now we can do collect here like so. Uh and then this now can be 0 50 99 99.99 100 and 150 um iteropied. In fact, this also can be iteropied. And then we'll do collect. It's still gonna complain down here. This uh assert this count zero. Great. Um we should just pass in quantiles. Actually, that's what we can do up here. We don't even need this. We can do this. And then we can do the same here. Uh, and this is going to be uh U64. Don't need a V. And this fails down here, too. Uh let's do dot collect there as well. And now these should pass. Let's hope so. Oh, fails because qual one at 579 oo because uh I can imagine it actually failing because the lengths aren't the same. Uh, but that's not what's failing, right? Because 1.5 is going to be turned into 1.0 and that's going to be a duplicate. So, why is it different? That is interesting indeed. Um, although we can, as someone suggested in chat, flip this one around so that it actually handles duplicates correctly. Uh, which we would do by Oh, which we would do by having this say. It would be a little ugly actually because then we would need to reread this inside the loop which makes me a little sad. Oh actually no we would not. We would simply say um I see what we do. We would do this um while we're less than the target then add the bin and do counts. Next uh and if if counts.next Next dot is none. Uh, no, that's not right either. We would say count is counts. Next else uh we would return some zero, right? That's the the thing we've landed on here, which I think is incorrect. Doesn't sound right to me, but okay. Um, then count here now is this. Ah, because we need the eye. Uh, we need to keep track of the I is going to be the annoying part. So, we're going to have to say let mute at count i is zero. Um, at count i is i. And then here we would say, so now we're basically in that if. So then we can do this. Um, and now I think duplicates should work. And now this can also change and say if the previous one is greater then let me stop. I should also only run the one I actually care about running uh which is data access. Data access. There we go. Uh it would be for the case where you want the iterators to be the same length. Give the wrong values for unsorted input. Uh in iterator on arrays is hidden by old rust editions. Yeah, I mean I forget what we've set the rust edition for in HR histogram. It's 2018, so I think this one's okay. Um, great. I think I like that a lot better. I do also want to push a cargo update actually to um to HR histogram, but we should do that separately. Okay. Uh we also need to update the docs here a little bit because it stops early if a quantile is not not greater than or equal to the previous IE provide quantiles ascending order and then we have to do the same here not greater than or equal to um and also our data access test now can have duplicates. So this can be minus 0.5 uh and 1.5 just minus 50 some duplicates. See that that still does the thing. Great. There's a lot of tidying that should happen in this repo. Maybe someone wants to be great and go through and tidy up all the all the lints we now have. Um, so if we look at this, I want to add source lib and test data access. Um, rewrite as iterator without allocations. Uh, you can't ever return the same result, right? What if two quantities should be in the same bucket? Uh I think that's fine, right? Because you would just compute the same result again because if uh if I read the next quantile and it's the same quantile as the one I just computed, then this I won't enter this at all because this was already met for the previous one. uh and the target hasn't changed because the quantile is the same. So I go here at count I has not changed. So this value is the same. So this value is the same. This value sets it to the same value. And so I returned the same result. And so it returns correctly returns the same result if the quantile is the same. So I think this correctly handles duplicates. Um, so let's do this. See if we can push. Excellent. Okay. So, back here. Um, [clears throat] took the liberty to update this directly with a zero allocation version. Um that instead just gives it to the caller to ensure that there that the values are in sorted order and simply truncates the return if they are not. Um the other thing is um HR his history does have a change log and so we need to make sure we actually add to the change log here. Um that return zero should be dead code. This um it corresponds to the case where you iterated all the buckets and haven't found the total you need yet which should be impossible because of the clamp. Yes, that is true. But do I want to make it unreachable? Uh unreachable. Um because target is clamped to total count. So by the time we so last bin must make this false, right? The la when you get to the last bin, the last bin is returned from here. That count must bring total to current index to be equal to total count. And since target is at most total count, the two must now be equal, which means this is not the case, which means you don't go in here, which means you must land here. I buy that. Oh, the compiler does not for an empty histogram. It's not true because there the total count is zero. So you don't even enter this. That's what this is for. So except in the case where the histogram is empty uh return some zero and then we can also do an assert e uh self total count is zero. There we go. Uh great. So we will no add source lib um explain sum zero case. Uh right. And then we also need to change the change log to say uh added uh HDR histogram uh value at quantiles and value at percentiles for faster lookup of multiple percentiles. Um, and this is in PR 138. Uh, and then now this is unhappy because of that. So, we'll do an update with rebase. Uh, this one we've already merged. That should be fine. This one should now run. And then we should be able to merge it. Yeah. Here's another thing that makes me think this is LMS. You wouldn't put 7.2x in here because the x depends on the number of values you look up. So this this is just an arbitrary number. Um, cool. Refresh so that I can hit approve. Um, and then I also want to reset hard. Go back to main get pull. Once we've merged this, I want to do an MR that uh bumps all of the dependencies uh in this project. At least the the cargo lock. We don't actually need to bump the major. So maybe we should do that, too. Um, and then I want to uh add batch Yeah, this stuff is all unnecessary. I can go there. Filipe reddis.com, but also ollie@codeperf.io. I'm confused. whi which which which email do they actually have? Um, also reddis.com is reddis using hr histogram because that's pretty fun if they are. Oh, really? Why does that hide the CI results? Come on. Um, cool. So, that one will leave baking in the background. Let's go look at this thing. Chunked skip scan and value at quantile supersedes 139. What is 139? Ah, this is 139. So, they opened a PR that they then decided to get rid of because the other one is faster. Okay, so we can ignore this one. It's handy one for one fewer PRs. Um, replaces the value at quantile prefix sum scan with a chunked skip scan. The old loop added one count and branched on the running total for every element. Yes. Uh this sums a fixed size chunk at a time reduction with no early exit that auto vectorizes and skips the whole chunk while its subtotal cannot reach a target. Only the single crossing chunk is walked element by element. I see. So instead of counting uh one bin at a time, you count eight bins at a time because that way you can use um vectorzed ops counter non- negative. So chunk subtotal cannot reach the target and it's no crossing element. Uh yep, that's fine. Okay. Yeah, that's that's this is definitely LLM. [laughter] It's like that's not even a question. Um, who calls it a white box par test? Comparing the chunk scan to a linear reference. Okay, that feels like maybe even more tests than we would want here, but let's take a look. Is this done yet? What's taking so long? Oh, yeah. Okay, the Mac OS build. Um, okay. Skip the test for now. I want to see the actual change. Okay. So, instead of walking the bins, we are now walking. I see the Yeah, the finish is just that's the this bit. That's fine. So we walk the chunks, we sum the chunk, and if we've passed the count, then we walk the chunk bits one by one to figure out exactly where we crossed. Otherwise, we just skip this the chunk entirely. And the tail is for if we don't evenly divide eight then we have to walk the remaining counts one by one. Uh yeah and this is unreachable. I agree. >> [laughter] >> The comment says that this point is never reached followed by unreachable with the same argument. Um yeah, I buy that that's faster. Basically you um sim over the counts. But why? This is not a useful test. This is just copy paste of the old code to see that the new code is the same. But we already have tests the test that that that that this function does the right thing. So this test file I just don't want. It's it's not necessary. The existing test already covered that this is correct. So we will kill those. That's easy enough. Is this done yet? 64. I'm basically I'm comfortable merging this. These are these are not going to fail if the others failed. We've made no um we have introduced use actually. So the 164 build might fail. Uh maybe even will fail. I think it will fail because I don't think we had use yet in 164. So then the question becomes, are we willing to bump the minimum supported rust version here? Mhm. There you go. Wait, it also Yeah. Yeah. Okay. Because it fails to even parse the file. [sighs] Okay. Uh cargo MSR. Nope. cargo MSRV find minimum of 168.0 and I want to use check. Just going to do a binary search here to find basically where am I allowed to start using use 182 uh rust 1.82 when did rust 182 came out? It came out in 2024. I am okay with bumping that. Uh workflow checks MSRV1 84. It is like it does make me a little sad because there's no real reason to do this because that there is a workaround we could do here that is technically backwards compatible. At the same time, I do think it's also uh like a two year almost twoyear-old Rust release I'm okay with bumping to if you want to have the latest version of this because I don't want to. This is a case where I would have to make the code a lot um uglier in order to not break old Rust versions. The alternative is there's a um um um there's a crate called Rust version um that lets you have yeah this um where you can say uh conditional compilation as of Rust version something but it adds a build step to your thing which I don't think we currently have uh build yeah we don't currently have a build r uh so this will be use of use. Wait, that's it should be 182. Uh, so someone in chat suggested instead of using use, you can just use plus a. Uh, the problem with plus a is it's wrong. Um, it it does sometimes or usually work, but it is is actually incorrect. Um, especially this has to do with variance. Um you're basically making the tick a here invariant. Um there's a the actual thing is there's a you introduce a trait called captures and you do this. Uh but that's not I don't want to I don't want to do it. This is an example of something where I don't think you should bump the minimum supported R version for arbitrary things. I think you should only bump it if it requires that you hold back the ergonomics of working on the development of the crate significantly. And I think this is an example of that. Um, MSRV for use of use. Uh note that this also bumps MSRD to 1.84 from no. 182 from 1.64. Uh since uh we make use of the use syntax uh which also means I need to change the change log. Um, that 1381.82 This doesn't that doesn't seem right. What what is supposedly the last commit here? 85. Oh no, that is that is right. Okay, get push. Cool. So now that's just going to go run again if we make it to run again. Um, and then we can go check out this one. and then get pull checkout one 40 chunk scan. So here I want to get RM source tests value at quantile scan and sort tests test value at quantile scan. Uh remove unnecessary remove uh I don't even know what to call these tests. uh unwarranted parody tests. And in fact, let's um we already fine. Uh we already have tests that cover value at quantiles quantile and friends. So no need to also check that the implementation equal to a copy pasted version of the old one. New paranoid non-regression tussia uh unsloppy maybe. Um and then the other thing I want to do is uh well obviously I want to change the change log um to say here that um uh HDR histogram value at quantile and value at percentile. Now use uh vectorized operations so that they should be significantly faster on larger histograms. This was number 150. Sorry, number 140. change entry. Uh and then for the actual implementation uh I think I also want at quantile chunks at a time can be done with Cindy. Cindy until we detect a chunk that passes the target in which case we walk. Uh only then do we walk the chunk bin by bin? Why is Clippy unhappy? Consider using as chunks instead. Why? returns arrays instead of slices. Sure as chunks. So chunks is now uh it's a slice of arrays of length eight followed by a slice jar. So this is chunks and tail and then this and this is not going to be remainder anymore. This is going to be tailor. Sure. Um if scan chunk does not perfectly divide uh the number of bins handle the leftovers some bins in chunks. There we go. And then this just to make it more readable. Comment can go away. Great. Simplify [sighs] and Clippy and better comments. Great. Now can I merge this see? I passed. Oh, a junk was stabilized in 188. Yeah, I would have to, wouldn't I? That's okay. Um, squash and merge with this description and this squash merge. This is the other reason actually why I suspect at this point I think I know that it was written by an LLM is that a human would not have caught they would have run clippy and went oh I should use as chunks changed it to ask chunks and then not have noticed that that would break CI um for the rust version in question. Uh so let's do here uh see main get pull this get merge main uh edit the change log because it's going to be um 1.88 S chunks. Uh and at this point I think we should also set uh rest version is 1.88. Yeah, someone said that in in chat too because uh um because at this point we do actually require that version of Rust in order to be compiled. Previously wasn't set because it was the reason we only supported 164 was actually not because of code in this crate but code in dependencies. Uh and so then the rust version uh version um and so if we had set rust version equals 164 we're actually lying because it supported I think down to 157 or something uh but you wouldn't be able to build because all the dependencies required that you upgraded but it wasn't our crate really that was the problem. Now it is and so now I think it is appropriate to set ROS version um uh bump and set MSRV for as chunks. Uh and then I think actually we need to update this uh this release to 1.88 because it uses a few newer Rust features like use and uh chunks. uh iterate on MSRV note and change log push. Okay, so that one's merged. This one's merged. This one is now going to run and then be merged. And then there's no one nothing else for this. Let's check if we have any other poll requests that are relevant. Uh bump clap. Don't think I care about that. Unrecord. No. Uh, bunch of old issues that we're not going to deal with right now. Um, cool. Uh, and so then we'll go to main, we'll pull and we'll do a cargo update. See, this is the other thing is like now the other thing that bothers me about setting rust version in cargo toml is when you run cargo update, uh the default behavior in cargo is to update dependencies only to ones that support 188. But I'm actually willing to have my cargo lock have a newer version of Rust in there, have newer dependencies, and then bump the MSRV in the um in the CI job to be a newer version of Rust for the purposes of CI. Um, but we'll um because I'd rather know that I'm running on an older version of a dependency. Um, but we will find out what that actually leads to. Okay, this uh the 188 passed. So now I'm pretty happy to just merge this one. Um, chunk skip scan invalued quantile goes away rather than the atom branch on every count element empty. I'm going to do that in here so that I get my little nice line wrapping and such uh on every every counts element bin uh sum a fixed size chunk of bins at a time. Um so that the compiler can doization. This is often significantly faster. Uh 150 observes no 140 observes. Um bins one at a time. Uh like so um also bumps MSRV to 1.88 88 in order to use as chunks. Much shorter commit message, but still means the same thing. I'm guessing this is someone watching the stream. Uh, this comment is somewhat inaccurate. We just iterate in chunks and that allows Yeah. Okay. Fine, fine, fine, fine, fine, fine, fine, fine, fine, fine, fine, fine. Uh, fine. Keeping me honest. What Cindy uh V chunks that they can be so the such that the operation can be auto vectorized by the compiler until we detect a tank. There we go. Yeah, chat is uh getting in my PR comments. It's good. It's good. I don't mind it. Uh techn technically correct is the best kind resolve. confirm and merge. Uh great. So now we have this uh we'll go back to main. I'll get pull um bump CI. So this is oh get remote uh add CI. So I have a repository where I keep my CI config. Uh, and so for basically every repo I have, I will merge that CI repo into here so that when I make modifications to my CI config, uh, it brings in like all of the various um, uh, all of the various dependencies, all of the various changes from that those CI changes so that I can do this merge CI. Um, and now I'm bringing in all the latest improvements I've made to my CI setup into this repo. Uh, and I, you know, it's it's git, so I actually get to do a merge. I get merge conflicts and all that good stuff. Um, so I can remove this because it's not used. I can remove that. Uh, let's look at check what has changed here. Um, I do indeed want my version. I want my version. Um sim confused about this part. What did I change here? I do want this instead of that. Ah, hack is moved is why. So there hack was up here. Now Simber is up here. Uh, simmer doesn't need to run on nightly, but it was also running a dock pass with uh config docs RS and that one I already have here. So, these bits can just go away. And then this should indeed change this way. uh and then should change to be this. So we add that uh and then we want to check scheduled what's changed there. Yep, I do indeed want that version. So, I've started um using the actual hashes for all my GitHub actions rather than just the version number because that way I can actually control when they get updated. If you specify something like v6 here, in theory, your CI job can change under you, right? Someone could push to that repo under that branch or that tag. Uh and now your job immediately picks up their change. Whereas, if it's pinned to commit, it will only happen when depends. Um which I'm more comfortable with at least. Uh, so I'm guessing a bunch of them are here. Those conflicts. Easy to fix. Easy to fix. Um, don't need OpenSSL, so that can go away. I don't keep the comment. This stays the same. Uh, why did I remove all features from this? Ah, for bench private. Yes. So indeed I do not want all features. So that can go away but I do want to keep the recorded the rest version. Now the one thing that's important when you do this is that when you merge uh bump CI repository files. Uh when you merge these, you have to not squash. You have to do an actual merge because otherwise you break the git history as the merges won't work in the future. You'll see this brings in a bunch of other contributors that are people who contributed to the CI. Um which like is maybe weird but is what I want to happen. Um so here specifically this needs to be a merge commit. Um but we'll wait for CI to pass there hopefully and then afterwards we'll do one where we um bumble the dependencies and then we'll do the actual release. This is why like open source maintenance takes a while, right? Like this is we're only on repository 2 for the day and we started 3 hours ago. There's like we're averaging about an hour and 45 minutes per repository. Uh, and it's not like there were lots of issues in either of them, right? Chat is the real LLM. It's true. Uh, great. So, we should now hopefully get this in soon. Uh, we'll go back to main and pull. Um, and we'll fork. We don't need to fork off that yet. Um, you also don't technically need to merge it with the merge commit because this there already is a merge commit. Uh, and so you can instead just do get reset- hard uh bump ci. So now you'll see this is at that same merge. Um, and so I can just get push to main, but that's only because I'm the maintainer, right? So instead of merging this way, I can just merge by pushing the merge commit I already have in this MR. Uh, and the only reason I open it as a PR is to actually see CI run and see that it's all green. Um, but I don't actually need to merge it through through this. uh that the other advantage of this is that I can now fork off of this and start working on some other branch and it will actually be forked off of the correct commit that will actually land um bump depths update and actually now we can um uh we could also upgrade the addition actually because we moved to we moved to one 188 and I forget what version that was. Rust 2024 was released as 185. So given we're now already at 188, we can move to Rust um uh Rust 2024 straight away. Um in fact, but I want to bump the dependencies first, I think. So you'll see there'll be some dependencies here where we don't actually get the latest version because we're behind on the majors. Um but luckily I think uh we can fix oops verbose. So serialization here this should be fine to bump the nom bump is probably annoying. Uh and the rand bump I think is probably okay. Rand is only a dev dependency but it will be slight. Actually, these updates are something I found LM really useful for is like I just tell it, hey, update to the latest versions of these, but go change check the change log for what you actually need to change, and they tend to do it really well. This is like a super annoying mechanical job. Uh, where LMS are actually super useful. In fact, maybe let's try that. Uh, it might be a good thing to stick in here. Uh, I do want this to land first, though. Just make sure I haven't broken anything. Uh, I will land the bumping the dependency separately, though. Uh, maybe not. Maybe I will just make that be one PR that bumps the cargo lock and then updates the the dependencies and then um updates the addition all in a row. I think we will do that. We should also fix the um the clippy lints. Why is coverage broken? Ah, coverage is broken because I haven't updated. Let's see conf Okay. So, workflows test coverage. This is still using the wrong version there. So let's um cd domini get pull github work with test coverage. So this no where's my coverage job? This we need to bump to um code cub actions. Surprised actually the dependabot has not picked up on this yet. Um so diff upgrade code cub to 0 0 push that to rest cic and then I will actually um this one's easy to redo back to main actually I'll go back to bump ci uh I will reset that Um, this is where JJ would be really nice because now in order to merge, I want my history to be nice. I don't want a second merge commit because that's kind of stupid. So, I will actually reset hard to origin main. Uh, then I will fetch CI. Then I will merge CI. Then I will do this and this. And then we have to fix up these again. And this one stays. That one goes away. This one stays. Right. What had to change here? Nothing. This can just go away. This does need to be this. And it needs to be quoted, but otherwise that's the correct one. Uh, and what's the last one? Scheduled. That one's easy. commit get push force release. So that should fix coverage. But what happened to the uh what I wanted to see was did this one ultimately pass? It passed everything except coverage. Uh, and I guess minimal versions hasn't finished yet. And minimal versions might not pass. That's fine. Uh, so now I'm okay. Um, I'm okay just uh merging that pumpci. So now this should have that one. Yep. Get push. Okay. So now we can go back to bump depths. Um we can do a cargo update. Uh we can do a get commit. Um update all the things. Uh origin bump deps and then we'll start a new PR. This one's now marked as merge because I pushed the merge commit directly to main. Um, that's just incorrect. Why is that? What's What's happening? Try again. There we go. So, we'll create the pull request so that we can start to get CI running. Um, and then I want uh cargo check. Does cargo check have a fix? I didn't think so. Um, a lot of the stuff is like in things that is a little bit annoying. Um but okay. So now we just need to wait for LSP to be happy here. So this says use max. That's easy. This says use max. Will cargo fix actually do this for me? All right. I think it will. We'll do all of them. Uh yeah. So this masks the lifetime from there. That this is fine. That's fine. That's fine. That's fine. Uh same thing. These are all just mass lifetimes. This changed in like a very long ago rust version. Um what does cargo check say now? What does cargo clippy say now? Uh cargo clippy fix. Fine. Cargo fix. Cargo clippy fix. Nope. Cargo diff. Uh read exact. Yeah, that's fine. That's fine. That's fine. Okay. Preix. Uh, now I want Oh, it's mad at interval log mod because of the indentation. Interval log mod here. I am also mad about that. Thanks, Clippy. That's not pedantic. That's just correct. Uh, great lib. This should be max. This should be max. That feels like something it could have changed for me. Should be max. and d serializer 167 check that all the tests still pass. They should. Um there's something that failed to to give me warnings here in the tests too. Uh so this is actually more more clippy fixes. Uh and then I want cargo fix uh all targets. Why was it not happy about that? Test data access 453. What do you mean? It's never Oh, this this indeed feels like there's a thing missing. That should assert something. I don't know what it should assert, but should assert 4228. Sure. Okay. What else do we have? Uh, example CLI Max. I see these fields are never read because the debug is not considered enough. But the debug might be printed by main. Presumably, yeah, the debug is the only reason we need these fields. So, I can do of unit type. Consider changing the field to be of unit type to suppress this warning while preserving the field numbering or remove the field. No, wait. Unless Yeah, cuz serialize returns one of those that gets bubbled to main which unwraps them. So they they need to exist. Great. Uh, lints in tests. Okay. And then we want cargo d-addition. Why did it only move? Oh, it only moves one edition at a time. That's fine. Okay. I think we need to we need to figure out what to do about these benches 2021 edition. cargo clippy. Uh I like using expect rather than allow for lints. Yeah, that's true. Uh expect uh isn't dead. The reason I thought allow here because is because technically it shouldn't even be surface a dead code because we do actually print out the debug representation here. So in theory Russ could realize that is true but then I guess we should remove the the lint. uh isn't dead, it's printed. Um yeah. Um, and now the question is what to do about all these benchmarks because there's a bunch of benchmarks in in HR histogram that uses um the bench support that existed in older versions of Rust. Uh, this has been like completely this is a nightly only thing that I think they're even planning to remove. Um, [clears throat] I think that's fine. I think we're just going to keep that. Um, so now if we look at cargo update, there's no updates except there are major updates. Um, and so here's what I now want. I now want to bump all of those to the latest version. Uh, the some some of them I think will be pretty easy like B 64. Some of them like the nom upgrade is probably actually going to be pretty annoying. Um, what I'll do is I'll push this first so that that makes it out the way. Why is coverage broken? Uh, oh, I need to add a token to this. That's fine. I'll do that separately. Don't want to do that on stream because I don't want to leak my tokens. Um, so here's what I want to do here. I'm going to go ahead and say, "Hey, Claude, um, bump the major version of each uh, dependency of this rust crate listed in cargo update-verbose. um for each upgrade uh for each dependency. Uh do the upgrade separately separately for each dependency. Um, and make sure you read the change log for the uh create in question to see exactly what changed and what you need to do uh to match the newer major version. Um, make a commit for each upgrade using commit writer before proceeding to the next create to upgrade next dependency and then in theory that can run in the background. Oh, I need to stop uh cargo format all because the fixed things don't know to format. Try again. [sighs] Okay, so that can now be happy run in the background. Um, and then once it's done, we add to the change log and then we can actually issue a new release of HR hisgram. Cool. So now this can just keep ticking on in the background. Uh, yes, that's fine. Uh, and we will in the meantime go to our notifications to see what there is next. Um, so Rust IMAP is a kind of interesting one because uh, Rust IMAP is a crate that I have effectively abandoned. Um, the the reason for this is basically I don't use it myself anymore. I know that it needs some pretty major rearchitecting. Like it's not async at the moment and I think it basically needs to be. Um, and I simply don't have the time to put into it to make it what it needs to be. Uh, and so there's a thread, the maintenance suggestions thread, where it's basically a thread about who can take over ownership for this. I've tried to pass along ownership of it for a while unsuccessfully. Uh, and in the meantime, there's like a bunch of different uh, upgrades and stuff that are just pending. Like someone needs to pick them up. Uh, and arguably I think the way to move forward with the Rust IMPAP is just for someone to write a new implementation and then just sort of import it into the same repo or we rename the repo and then they take over the crate, issue a new ma major um major version that is effectively just a different crate. Um, but actually doing that handover is is non-trivial. So what I'll do is I'll open each of the Rust IMAP things and then many of them we will just close out. Um, and then they'll reply to the to the maintenance thread. So like this one for example, I'm just going to mark as done. Uh, this is command injection. Yeah, I mean there's a there's a bunch of things like this. This is a very very long Okay. >> So, I guess this is Yeah. So, someone has basically posted a uh an example of something that allows you to inject commands into um uh into IMAP operations uh because they're not sufficiently sanitized, which or validated, which I think is true. I mean, rust map was never really written under the assumption that you would have untrusted inputs into the commands, but it is true that if you do, it doesn't really guard you against it very well. Um, there are fixes to this, but again, the the whole point of saying I someone else needs to take over this crate is that I will not be maintaining it. Um, so I'll mark that as done. I'll mark this as done. Uh this is this is IMAP memory exhaustion. Yeah, I mean I don't it should be fixed but I don't care. Um this is the problem of abandoning a crate, right? It's like there are thing there are things there are many other things too where I'm like I know this needs to be fixed but I cannot be the one to fix them. Let's just check if this uh yes fine. Um right. So is actually one of the people who have contributed to this crate in the past um and who's one of the people who who might uh take over maintenance of it. They basically built their own rust crate um that that implements IMAP and does async and they use it in a um in an application and everything. So it's a pretty good candidate. Um, a lot of the discussion that's happened on this issue is basically they've described the way they've implemented that crate. Um, and I think what I wrote somewhere, let me see if I can find um, uh, yeah, for a while the delay was like they weren't ready to open source that crate for whatever reason. And then there's a discussion about like the way it was implemented and everything. And I think what I wrote somewhere was um up here uh >> yeah and what I've written here too right is that so I've already added two people as maintainers of the crate on GitHub um but they don't have publishing privileges yet so instead um instead that's sort of the last thing that I hold back right is like you can't publish under this name until we have decided this the path forward but you can go like now contributing to the GitHub and everything because I am not maintaining it. Um yeah and then part of the debate here is like how should this be implemented? So we had some exchanges over email too where I was basically like I don't think the way they've implemented their replacement crate is the way I would have reimplemented it. But I think I also wrote like I it that should not be the blocker, right? Like think of this as I've looked at the way you want to do it. That's not how I would have done it, but I am also not the maintainer. So I should not be a blocker for if you want to do it this way. Um like I would much rather someone take over the IMAP crate name and bring it back to life than that we now have two crates, one of which is abandoned but has the the the sort of standard name. Um, and so I think that's basically where we've landed here. Let's see. So I finally released IO IMAP. It sits on top of the IMAP types and IMAP codec. Exposes three APIs. IO free co- routines, a stood client, uh, and a full stood client. Um, yeah. So the sonio approach is the one that I'm I'm skeptical of. Um, but they've found it to be good. It's almost in production in a CLI and a TUI a mail synchronizer mailbox watcher. Uh it's more complex and verbose but usually you won't face it because it's hidden behind the client. It is true like the what sense IO does is basically you implement the whole state machine of the protocol and all the parsing and stuff but you don't actually implement any of the IO. So you don't implement interaction with um TCP or with TLS like all of that stuff you leave to the callers and then those are the like basically think of it as implementers of read and write are sort of passed down the whole stack. Good job. You can leave. Sorry, a cat needed to leave. Um, so you pass the the read and write down through the stack and then the the sort of the state machine and the uh the protocol implementations like the parsers and and stuff uh are all like you can think of it as generic over the IO type. Um, and so then you have a client that then sort of instantiates the co- routines or these these parsers and and and um protocol implementations with concrete implementations of IO like like TLS or like TCP. Um, [clears throat] and so yeah, I guess they're basically saying it's sad that it would be split and that was never my intent. My intent was that the if if you have a way you want to maintain it going forward, then please maintain it that way. Like I don't want to be in the way of doing that. It was just giving sort of my um uh my opinions on that design. It was not intended to be blocking. Uh so let's make that clear. Uh, I want to get back to you that the design is working out. uh certainly a good um indicator might be uh a decent path forwards. Um as I wrote above, please don't let my preferences around implementation um in the way in the way of Let me find the crates here. I'm perfectly happy uh for you to basically delete um all the code in this repository and replace with your implementation under the create name of IMAP. Uh, as long as there's a new major version, there's a new major version. Uh, and ideally a mig a migration guide. Um uh I'll also then hand over um privileges on Chris.io for the IMAP name. Uh once you feel like uh you're wait to do that switch over cuz then a bunch of uh current users of IMAP will come knocking. Nope. Uh uh what overhead does that incur if you swap out IO types and APIs with different memory models? It's not with different memory models. it's that you um you basically make the entire implementation generic over the IO or or have like they're co- routines. So basically they they operate over they they're monads if you will over the IO mon they're not quite monads but kind of monads. Um so they um you instantiate them with an IO and then they do their IO through that. Um and so the the overhead is more that you have to construct the state machine entirely without the the IO parts which tends to mean more buffering um more allocations more keeping things temporarily because um if if you have the the IO things like hardcoded in your code paths like you have a TCP thing um when you want to write something out you just write it directly to TCP. uh here you kind of need to store the things that you will write to the IO when you have one. Um so that like when you when the when drive is called on the state machine then you write out the things because only then do you have the IO and so that there is more of that sort of um uh the sort of intermediate steps that you end up having to implement and the API also as a result ends up more complicated and the implementation ends up more complicated. Now, as as um Soywood points out here as well uh correctly, the API for end users doesn't necessarily get more complicated because you can you can implement a a crate that wraps the Sans.io crate um such that it has an an API that just uses TCP and TLS and then under the hood you have to manage all that complexity, but the outer interface can be a relatively standard IMAP interface like a synchronous or asynchronous one. Um, and so it's more that it introduces sort of cognitive and performance overhead. It's not the fact that it's it's a bad design, right? Because it does buy you something which is this ability to be to abstract over the IO mechanism such that it can be not just TCP or TLS, but also things like async or not async. You basically take the whole driving of the IO part away from the implementation of the protocols and the uh and the the data formats. Um, cool. Replied. Nothing more to do on IMAP. Uh, let's now see how Claude is doing. Oh, no. I normally auto mode is better at this. Oh, that's cuz I don't have sandbox on. Sandbox with autoallow. Thank you. And I will keep let that run in the background. So, this is going to keep updating all the things. It's still unhappy about coverage. Um I guess I let me see if I'm able to fix coverage without uh HDR In his Instagram rust uh without leaking the token. That is the question actually. I wonder is that token already there? Uh secrets dependabot. Yeah. So that's missing. So uh [clears throat] and this is still branch is still named master. No branch is named main. main. Yeah. See, I want it to not show that token. Uh, which I can do by doing this so you can't see it. Closing this to new repository secret. Uh, and then also doing this in a different window so you can't see it. Sorry folks. Regenerate this token. So the old one is no longer valid. And then putting that into this uh to here and into where else does it need to go? It needs to go into actions. repository secrets and here. And so now if I go back here, you'll see there is now a code token. And so in theory, currently stealing my credentials. Yeah, I know, right? Uh hopefully I prevented that from being the case. And so now I think if I try to rerun this one. Oh, I'll have to wait for I'll have to wait for this guy to finish. Why is that something makes this one really slow? which makes me think that there's some dependency where there's like a performance problem in one of the dependencies that we happen to hit in this test. And so it's not that we don't work, it's that if you have the minimal versions of things, uh then this particular work um flow is really slow. Uh which I think is okay. But I can't kick off the coverage job again until this job either succeeds or fails. So I guess I will kill it. Cancel. And then once that's killed, I can start the other one. All right. How's this doing? Uh, yeah, that's fine. Cloud reading from NOM. Yeah. So, this is what I was expecting, right? for basic uh yes for B 64 uh it's really just a simple version bump because we're not using very much of the B 64's APIs um the the interface there that we use is is very very limited uh isn't the dependabot secret just from PRs from dependabot not from normal PRs don't you need it as an action secret yes so I added it for both um you're just trying to get me to show you the secrets uh here I'll show you but I will first kick off this one. Rerun failed jobs. Rerun. So, if I go back to here now, uh, and go to secrets, you'll see there's a dependabot secret right there, but there's also an action secret right there. So now maybe coverage will now be happy. We'll find out. uh and then we will see how this is progressing. Rand is the biggest surface. It is used a lot but most of them should be pretty simple I think. Yeah, it's mostly renames I think in Rand I run claw directly on the prod server. [laughter] No. Um I it really depends on the task for this kind of stuff. I've found the the sandbox is okay. Um but you know it the the risk goes up every day, right? Because the chance of poisoning attacks goes up and the sophistication of the poisoning attacks go up. Um but in general the sandboxing u seems to be pretty okay. The problem is the autoallow, right? It's like if you autoallow things to escape the sandbox, well then the sandbox isn't doing that much. Um but at the same time without the autoallow it can be extremely painful um to um to not have to like approve things all the time. Um but for like bigger jobs and stuff I will run it like basically in a VM or in a container. Uh ah I didn't press show secret so that's why the test is slow. I think you're right. I think you're totally right. Uh, were you ever curious about feeding the IMAP crate to Claude? Make this async don't make mistake. Make code reviewers cheer. I have thought about it. The problem is I think it's a I think it's such a substantial rework that you would basically just throw away the existing um you would throw away the entire existing codebase. I don't think you would reuse it. Uh, I don't know if I'm being unnecessarily paranoid, but I don't trust the AI companies at all. I don't trust their built-in security stuff to even be sound. Yes and no. Right. So, the the sandboxing is basically just um uh using something like bubble wrap. Um, so it's basically enforcing what it's creating a little jail for the process effectively for what things it's allowed to read and write and what um what network requests it's allowed to do and that sort of stuff. So the sandboxing is actually pretty decent. Um the the problem is the autoallow, right? So if the process has the ability to say run this command outside of the sandbox without prompting, that kind of ruins things. Um, although interestingly enough, I'm not sure I follow why uh why it still keeps prompting me because this should be caught by the sandbox already. I think it's maybe because I enabled the sandbox after I kicked off the work. Uh, yeah, that's fine. Because in general, I found that the this kind of like approving lots of small like prompts that are just read only and there's no reason to really prompt like this is this is the kind of thing that makes you not pay attention as well to the things that you're approving and therefore makes your security worse. I would much rather it prompt me less often such that I actually look at the things that it prompts me with in more detail. Um, I can press switch tap to switch to auto, but I want it to be in plan mode because I want it to be planning. Um, hence the the problem. What? But what model am I? Oh, I know the problem. Why am I using sonnet 5? That's not because I've also found sonnet to be much worse at issuing commands that aren't silly. Uh, [sighs] I'm going to actually stop this one. I'm going to switch the model to fable. Uh, plan mode sandbox. Yes. And then try that again because the prompting makes it so much slower and Fable is a lot better at planning. I mean, Opus is too. Uh but sonnet is not very good at that. Uh don't ask him for Gary update. Uh don't ask for index crates io. So I found fable to be better at using the tools such that I don't have to hit yes as much for like randomly generated bash scripts. Um I've not tried um Saul. No, I I'm only really using cloud. I'm not um I haven't worked much with uh with codecs for example at all. There we go. See, this is a much more reasonable way to approach it. Rather than try to pull them out of the cargo registry that's locally on my computer, just like fetch them directly from the GitHub page of each repository and then boom. And then you see the auto mode classifier is a lot happier about let letting things just sort of pass through. Um, cool. Now we got code coverage. report is 55 commits behind head on main. That doesn't sound right. Oh, is the CI things. I'm confused about why it claims we're behind. Um, I suppose actually I could merge this and then have the major changes in separate commits, but I don't think I care. I think I want the major ones. Um, but yeah, we'll we'll see. It'll be interesting actually to see how annoying these changes end up being cuz my experience in general has been that um catching up with major bumps and crates like this is a lot of like look up the change log, look up exactly what changed, look at what I have, change the syntax slightly here and there and here and there and here and there to match up and then run cargo check and cargo test and then find whether it works and then do it again and do it again and do it again. and it's like extremely annoying manual labor that can just like can usually just happen in the background while I'm doing the other things. Um, and so this has at least traditionally been things that I basically uh it just saves me time. Um, let's see what we have here. Uh, yep yep those are indeed the updates. So print each dependency separately. It is very important though to review the plans because often they make very little sense. Um, adapting the code to each new major per its change log with one commit per upgrade. All other dependencies are already at the latest majors. Yep. Uh, B 64 should be trivial because yeah, basically nothing. NOM 78 is substantial because oh combinators right forgot about this. So nom which is the parser generator crate moved from a sort of functional syntax where you have these combinators functions and now those have gone away and instead there's a trait called parser so that you move from this this is the old style to this which is the new style. Um and so you basically need to like walk anywhere you use any kind of non- combinator and make this change. Um, and you also the the input mechanisms change and so it's like still a mechanical change but a really annoying one. Um, so yeah, mechanical change is expected. Shouldn't be too bad I think except for the fact that it's mechanical but not trivial with said. Um, and then rand is mostly a bunch of renames. Uh, small RNG goes away. That's fine. Yeah, this seems totally fine. Num, uh, we only really use num in um, serialization. Uh, so that should be fine. Yep. In order increased risk. So, we'll do B 64 then random nom. That seems reasonable. Uh, we'll use cargo ad. Not technically necessary, but it's something I put in my cargo toml because it tends to get confused about which version to put in there because it doesn't always pick the latest one. Um, as a cargo ad just does that more reasonably. Adapt the code per the change log letting the compiler drives the exact renames. Um, verify that everything works. Yes, except I don't want with all features. So, that is something I need to tell it. Um, all features won't work since uh that enables nightly only things. Um, but let's do this and see if that works. Uh you will need to use cargo plus nightly test uh in order for all features to be usable. Yeah, mechanical but not reg friendly is perfect for agents. It's like agent food. Um, seems like a good use of a clawed workflow. Yeah, I think this kind of like upgrade the majors is something that you could pretty easily turn into a um, uh, you could pretty easily turn into a pretty standardized workflow that you just have a well- definfined prompt for. Um, what terminal and desktop environment are you using? This is uh what am I using now? It's a good question. Oh, let me just approve this first. Uh let me check what it made down here. Yes. And use auto mode. Um so right now I'm using Sway um for the actual uh desktop environment and and window management. Um and then I have like DUN for as a launcher. Uh and I'm decently happy with that. Um terminal is elacrity. I'm running T-Max in there with the fish shell. So nothing has changed in my setup there. Um, I'm generally fairly negative on AI, not because the tech is inherently bad, though there is some issues, but the main problem is most people just turn off their brain while they're using it. I think that's very true. At the same time, I also think one of the best uses for like the the agentic coding agents is for work where if you did it manually, you'd kind of turn your brain off anyway, like this kind of um of like major major dependency bumps where if I did this manually, it would be a similar like very mechanical affair where I it would just become sort of roach repetition and I wouldn't really be looking that carefully at every change anyway. So, I'd actually rather have this run in the background, me do something else, like talking to you about other things, um, and then review the result to see that, okay, now I can look at the result and actually focus on them and see whether this looks reasonable. Um, makes me feel better to see John using AI. There's a decent level of guilt behind it for me sometimes. You know, I I I don't feel guilty at all about using AI for for things where I feel like it's an appropriate use, right? Like again this kind of mechanical change for example or there's some amount of like uh you know turning PDFs of specs into initial implementations like there are bunch of those kinds of things where they can be extremely useful. Uh I've also found them very useful for debugging. Um I think they're really good at finding bugs not so good at fixing them but really good at debugging. Um, I also found them decently good at like exploring new code bases, uh, and helping me ask questions about how things fit together. Um, that they can really speed you up there or from gathering like domain context that I don't have. Like if I'm reading a the source code for a crate where, you know, I know Rust pretty well, but I don't necessarily know the domain of the crate very well. And so I can use its general knowledge to like augment my understanding of the Rust code that I'm reading. Um, so there's a bunch of those kinds of things where I think it works really well and I don't think there's any reason really to sort of feel shame about using it in that uh in that way. Um, code review too, it can work pretty well, although it usually is not sufficient, but it is a a useful step. It can also be a really useful first step before you use humans time to review. Like one of the things that that I've seen especially at work actually is it's really useful if people go through the sort of automated code review or AI based code review before I go review it because otherwise I will go through and leave like 50 comments which takes a lot of time. If they instead do like 45 of those the AI can find and point out to them and they can fix them before it reaches sort of my desk. That's way better for me. it's a way better use of my time to read through and not have to comment on all the nits. Um, and so for that kind of thing also, I think it's it's super useful. Um, it's when you start using it for like the sort of turn your brain off for something where your brain really needed to be on um where it gets frustrating or where it becomes sort of loweffort PR contributions like some of the ones we looked at earlier today, right, where um the the observation was correct, right, that this could be made better. Um, the feature request, if you will, was a was a good one. Um, the implementation was not wrong, but it's just not what I would have wanted and not I feel like if you think through it a little bit, that's not really the way to expose it. That's not really the way to implement it. That's certainly not the way to test it for for some of the the like parody tests that was in there. Um, and the the PR description write up was like not super useful, very verbose. And for those kinds of things that that is where I would have some amount of like I guess shame is the right word where I'm like do one step better than that. Like think about if you're the receiving end of this was was this the best you could provide for me to then review? And I think the answer to that is no. Um that doesn't mean that it would be better if you did nothing because I'm very glad they send those contributions and send those um identifications. That's great, right? Like now HR histogram is better as a result, but the way you do it matters, right? It it matters that you um didn't go that extra step. Maybe it would actually been better if you just like filed an issue that said, "Hey, can you make this change?" Uh rather than the autogenerated change that I basically had to rewrite manually anyway. Um, [snorts] but I don't think there should be a sort of unanimous like um AI is bad for everything and and never always something you should be ashamed of. I I I simply don't think it's true. Um, [clears throat] do you think junior devs are cooked because of AI? Like is there hope? I I I don't think they're cooked at all. I think they're, as I mentioned, there's a bunch of things they're good at. There's also a bunch of things they're really bad at. Um, and where I think you need to have like um an understanding of like how these things fit together about what you're trying to accomplish, what the you end user's goal is, what your future goal is for this codebase. Um, I also find that LLMs make a bunch of mistakes where they try to replicate patterns even when the patterns are not necessarily appropriate. Um, there's also a bunch of places where there are no existing patterns to necessarily map well onto your codebase. And so I I just don't think the engineering job is going away. I do think that there are many engineering tasks that would be reduced. I also think there are some engineering tasks that will go away. And so if your job is only those tasks, then yes, it will be a problem. Like you know, if you run, I don't know, a business that um builds custom WordPress templates, I think you're in for a bad time. the company might be in for a good time, but you as an individual developer doing that development, I think AI basically does that job. Um, and so it's not clear that that is a um that that you're in in like a safe work environment. Uh that that your position is safe. But for a lot of the like back-end software engineering like distributed systems, concurrency, um safety critical systems, financial systems, like I I just don't think that it replaces the engineers. Um and then people just accept the result as it's perfect unless it's instantly errors. Uh it's really a problem with computers being math machines that rarely make mistakes to normal people. I do think that people have a sort of instinct for if it's done by a computer, it's probably right. That no longer holds in the face of AI. I think that's less of a problem for engineers. Maybe more so for engineers that are newer to engineering, but certainly for experienced engineers, I don't think they tend to believe what computers say just because they say it because we're so used to computers making mistakes if we're programmers, right? Um, the guilt for me is the ethics. Licensed open source code is basically ignored by all of the AI companies and we all know they're just using scraped open source code. So, so I I'm actually less concerned about this certainly for for for programming because the reason I put my code out in public is not because I want to claim ownership of it. And if other people learn from it, that only makes me happy, right? Uh, and if the LLMs are trained on it, well, okay, that just means that I now get a tool that's better suited for how I think code should be written because my code is represented in what it does. U, so I have much less of a concern there. Um, yeah, they're also really bad at designing abstractions and removing code. It's true. Um, I hate AI discourse. Just the worst opinions flying around. I mean, that is the other problem. I think as a society, I don't think we've figured out how to even talk about AI because there are very strong opinions on both sides of the debate. Uh, and there's not a it doesn't feel like the whole there's a whole lot of room for nuance. Uh, and I I think that's really unfortunate because I think the nuance is where the reality lies. Um, okay. Uh, let's see. So, it's done. All three major bumps are done. Each in its own commit. Uh, cargo update dry run verbose now reports nothing held back. B64 only used the engine API which is unchanged. Rand uh did a bunch of renames. And the now fallible uh now fallible uniform new that we call expect on that's fine because rand is a dev dependency. We only use it in tests anyway. The removed smallg cargo feature was dropped in rand variant. I collapse the nine hand unrolled uniform new entries into stood array from fn rather than repeating expect nine times preserving the historical quirk that the nine byte range excludes U64 max. What we we have to look at that looks weird. Um upgrade to nom 8 pars rate migration turn out smaller than the change like suggest only true combinators need to need the new form leaf one still return callable closures replace the deprecated is digit with asky digit and we got rid of minimal exagert explicit basic for nom depth nom feature entries which are removed to keep the implicit feature consistent Uh yes, I don't think we care about maintaining that but let's go look. So let's first start in cargo toml. So in cargo toml uh I do actually want these to just enable the depths. Uh but I don't think that matters here because we don't it's not the case that we want a feature by the same name. Uh although we can we can opt into that because there's there's no real downside to it. Okay. uh flight nom, but I want nom to be I want these to be fully specified versions. Um because otherwise we might accidentally uh uh update rep4. What? Fine. Lock basis for uh so 02d1 uh num should be 8 0 0. It's just in case we accidentally are now relying on something that came in like 801 nom for example then our minimal versions check would would break. Uh and rand is 0102. should arguably do the same for for the other dependencies too. Um, okay. So add cargo toml uh make minimal versions explicit. Um, right. Let's go ahead and go back and show dashp. No, get log P and see what we got. So, let's go back far enough to that's rand. Where's the first one? Here. B 64. Uh, we only use the engine API which is unchanged. So, we move to 022. That's all. Cool. That's easy. uh for Rand. Uh yeah, small RNG now becomes make RNG. It's fine. Didn't really matter. There was small RNG in the first place anyway. Um, but we need to look at this rand varant stuff. Okay, so this we fixed. Uh, this is changed to make RNG. That's fine. That can stay that way. Make RNG. Make RNG. Make RNG. Make RNG. Uh, uniform new is now fallible. Um, but we just use expect, which is fine because this is in tests anyway. So that all seems like a fairly mechanical change. More make uh gen range to random range that seems fine. Ah, so here we had several hardcoded ranges. that I've now changed into I see bite length is I + 1. Yeah. So that's going to be this guy. So smallest is that one. Largest is that one. Night bite ranges upper bound is UC4 max. So plus one would overflow. Give the historical behavior of excluding U64 itself rather than switching to new inclusive. Ah you see here for nine we don't add plus one. That's fine. But it feels like we should it does feel like um new inclusive is better because new include that that's really what this is trying to do, right? Is uh this is trying to make it inclusive of the largest number. And it just happens that for UC4 max that you don't plus one. So I think we should just move this to um new inclusive and that way this a lot of this comment can go away. Uh range pix uniform from 0 to 9. Okay that seems fine. Okay. Uh, these all seem pretty reasonable. Great. Okay. And then the last change is the nom change. Dependency create dependency tree shrinks. That's nice. We now do the mechanical change to parse. uh and asky is digit. Technically this one can be simplified slightly but that's fine. Um so let's then do uh in a new commit uh move to uniform [clears throat] new inclusive for the nine invite arrays. Um and then I think we'll want cargo format. We'll want a cargo clicky pass. I think some things there didn't look quite right to me. Um, but now we got to just have that happen in the background rather than that would actually have been a pretty annoying thing that like would have taken an hour in the background. Yeah, exactly. So here, um, this this now simplified to just be new inclusive, which is what the old thing was trying to capture. Um chat is going on about AI, but they're going on with each other. Uh okay. So show dashp include U64 max in random number var. Great. Now let's just see that that still passes. It does. Uh let's check that there's no cargo update to apply. There shouldn't be. Uh car we can remove session.mmd. We can cargo format all. Uh we can cargo clippy all targets. It's probably not happy about all targets. Uh can we make it fix those? Let's make it fix those. Why did it not fix the max value things? Like why is max value to max not an autofix sometimes? set is all you need. Uh ah there's another one. I 64 max value and I64 min value. What else do we got? Okay. You I suppose I could have just matched anything here, but uh you min max. I see a U16 up there too. Yeah, of course. Uh, source reser interval log tests line 261. Thank you, Clippy. And then it is unhappy about some other things. Tests testators I agree I don't like that at all. Okay. Get those away from me. Oh, nightly night. I can't spell nightly uh test serialization 58 and interval log 326. Great. format all bunch of clippy things. Okay. Uh let's run the test suite just to make sure nothing silly happened. Excellent. Commit um cargoy all targets get push. Um, this is the other thing I think is important is that when you use AR for some of these things, like leave Claude as a co-author, make it very clear that that part of the change was made by an LLM because that way it's also clear to someone looking through, I did these ones, the AI did these ones. I think it actually matters. Uh, cool. So, once this is done, I think we're in a pretty good place. I think then we can finally do the HR histogram release. Uh, and then I think we'll probably end the stream there. So, we only we only actually got through two crates. Well, three if you count IMAP. I wanted to also get through um inferno, but I don't think there's too much there. Uh, let's let's let's look real quick. Maybe we'll get that out of the way, too. It's only three for Inferno, I think. Why are there a bunch more now for HR histogram? Oh no. Oh no. Right. Uh I forgot I sorted oldest and newest. And so now there's a bunch of newest comments on the ones that we just landed. I guess we should tidy those up at the end too. So uh the release here, that's fine. Uh this is already addressed. Uh ah so now because HDR histogram hadn't had a commit for like three years the pendot wasn't running and so therefore it wasn't proposing updates but now it is proposing updates right so it's trying to bump nom which we've already done it's trying to bump a couple of GitHub actions um which I'm fine with um so we can actually land these that'll be fine Um, the nom one is going to go away though the moment we land this PR. Um, I don't want to land these until that lands because I'd rather them be um rebased on top. Uh, and then we have I'm assuming these are comments from you all. Uh, here the quantile changes but the previous quantile stays the same. Yeah, that's that's a good point. Um, it is true that technically we could start returning sum after returning none. I don't think it matters as as you point out as well because the contract for iterators is once you call none uh there should be no assumption that anything that comes after that is valid. Um, I guess we can um we could fuse the iterator. Alternatively, we could just override um actually it's also kind of reasonable, right, that if you if you have something that's invalid, we return none. If the next thing is then valid again, I'm okay with us continuing to to return the same thing. Uh as you say [clears throat] since the contract for iterator uh beyond on uh that you don't cause UB which we don't in fact arguably it is more useful that the iterator returns to a usable state um uh after it returns none for a an out of order uh quantile This is also interesting because this is the kind of thing that I think an LLM review of this PR might have caught. Uh, okay. So, we can mark that as done. Took me long enough. Uh, more updates coming in. Uh, in 142 + 140 and what were the other ones? 138 and 140. Great. Um, yeah. Then we'll look at Inferno in a second. I don't want to context switch to it quite yet. Ah, so there is one more Inferno one that needs to go over here. Oh, it's unhappy with something. See is unhappy. Oh, it thinks we needed to bump to a new major version. Uh feature has been removed. The feature B 64 nom flate and crossbeam channel. That's fine. Um these these it is true that those are technically gone because so in in older um rust version I can close this. In older Rust versions um when you didn't have this any optional dependency like here flake 2 for example is optional any optional dependency automatically became a feature that consumers could in theory enable. Um it's not just these it's also any optional dependency. Once we made this change then now technically um those features those sort of implied features go away and so someone could in their cargo tumml have a dependency on our crate where they write features equals fleet 2. It is possible in which case we will now be breaking them by issuing this new release. Uh but if they do I don't think this is actually part of the contract this the the sort of implied features here. So I'm okay with this particular breakage. Um, okay. And then patch these shouldn't m minimal versions is probably just going to keep hanging. Yeah, it does make me slightly upset um that minimal versions now just hangs because that's going to be annoying for CI going forward too. uh test sync no drop. So that is using sync histogram which does use crossbeam channel. So I wonder if the way to fix this is actually um Yeah. So, it's probably using a pretty old cross beam channel. So, cross beam channel. So, if I do cargo nightly update minimal versions, then what version of cross beam channel do I get? 050. So interesting. I wonder that supports Russ version 160. So I think we just bump cross beam channel to 0516 and see if that fixes the CI here. Um because it's specifically in the the sync tests which use cross beam channels. So I'm going to try that. I'm going to do cross beam channel 05. We do 15, right? Like uh because 16 was just released. Oh, 14, 13, and 12 were yanked. [snorts] 0 is not yanked, but it kind of feels like it should. It's probably I don't know why 51 would be yanked. Um 515 is over a year ago. So, what changed in 516? I'm inclined to say 515 so that we don't force people to update to the latest because some people like to have an update policy where they only update to things that have were released at least 3 weeks ago or something for supply chain security reasons. Um but let's go ahead and see what in crossbeam channel changed support for rust analyzer autocomp completion and make never constant. So those don't matter to us. So let's go ahead and try 0515 cargo update get diff uh try to fix minimal versions. We'll see if that makes things more happy. chat is having [laughter] an extended discussion of AI and how it relates to Clippy uh LSP improvements inside the macro do nice in general. Uh I was melding at that with Tokyo. Yeah, it's true. LSP improvements for macros are very very useful. is more it doesn't matter to us for this change because first of all we don't even use that macro from the channel stuff in this crate. Uh and so I don't really want to mandate that people upgrade to at least that version of crossbeam channel just because they wanted to update their um HDR histogram. Like technically this could be a lower version. It's just this is just to make u minimal versions happy. So in fact if I pull that up here I'm curious to see whether that uh does the right thing. And then in fact we can actually do the release in this PR2. I think maybe that's what I'll do. So we'll do um we'll go look at cargo toml and we'll say this is going to be 760. Uh so I'm going to issue a a new minor release not a patch because we're bumping the Rust edition, the minimum supported Rust version and the major of a bunch of dependencies. So this feels like it warrants a minor here. Um and actually and we're adding um a new external feature which is the uh value value at quantiles and value at percentiles. Um, so now if I go to the change log, I also need to do this. This is going to be 0. No, this is going to be 7.6.0 released 2026 uh 0718. Nothing was removed. Yeah. So there's it's 3 years since the last release, but the crate is still alive. Actually, 75. So we're actually missing links for 753 and 754. So this should be 3 4 6 0. So this should be 2 to 3. This should be 3 to 4. This should be 4 to 6. And this should be six to head. Uh why not major if the minimum supported Rust version increased? So um I don't consider upgrading bumping the minimum supported Rust version as a major change to a crate. Um, I think that is that puts too much strain on the consumers of the crate because for most consumers there's nothing they need to do. All they have to do is like run cargo update and things will just work. If I make this a major change, it means that no one will get these updates u including like the vectorzation updates that speed up a value at percentile unless they change to 8 point something which is just unnecessary. Um I I I don't generally consider um bumping the MS MSRV to to be be major enough to warrant a breaking change. Um cool. So get commit a release as Z 7.6.0. Minimal versions passed. I was right. It wasn't crosspin channel. Excellent. Um, and then here actually did it say wanted major or did it say it wanted minor? Yeah, major. Okay, that's fine. Um, so now we will do edit um release 7.6.0 with all of the updates. Um, so 6.0 material with the various things already listed in the change log plus uh cargo update. uh 2024 edition uh base 64 nom and rand updated updated to latest major uh rust and clippy lints fixed. Uh what else did we do? and coverage fixed. Technically, that was fixed elsewhere, but it's fine. Uh, if if this fails, I'll be very upset. It really should not. This releases Okay, so now we just did that one and then we can squash this. I don't actually care about maintaining all the individual commits here. I think uh maybe I do because we do have these in here that are kind of handy to keep separate if we ever need to do um regression testing. Uh yeah, I think I'm going to make this a merge commit. [snorts] I like keeping history. Come on, Mac Runner. I believe in you. You can do it. Do you believe in the Mac OS runners? It's been running for six minutes. Come on. Even the Windows runner is finished. Windows runner finished in one minute. Mac OS, what are you doing? What are you doing? You're just compiling the last crate. What does that even mean? There we go. All right. Come on. Come on. Be green. Be green. Be green. Yes. That looks promising. Ha. Okay. Green. It's green except for simmer which is fine. So now we merge this one. No. A commit message is wrong. The commit message is wrong. That's fine. That's just the merge message. It does have the That's fine. It's fine. It's fine. It's fine. But the the OCD in me is is uh very upset. Okay. Uh so then we go back here. We run this script, which I really should open source somewhere. Uh and then we do uh GC main. No. Uh get tag-dv7.6. I lied. I need to first pull on main then tag uh then cargo publish and get push tags. Yes, that's fine. What? I didn't. [sighs] Okay, let's go ahead and uh undo that one. Um, get reset hard to 3733. No. Um, I want to see where we forked off. Yeah, we forked off at Bumpsy. Okay. Reset hard bumpi push. Force release. Undo the merge. Undo the merge. Undo. Okay. Yes. Yes. I know. I have said this is not allowed. And yet here I am being unwilling. Allow force push specifically John because John can be trusted. Use pass key. Push it. What? Yeah. Bypassed. Great. Now we go back here. What do you mean? No one should be allowed to force push. Whoever needed to do that. There we go. It's safe again. I was worried there for a second. Get push. Uh back to my pull request. This one which is now uh going to be uh bump deps this one new pull request. This one is now going to be called this because I don't think there's a way to reopen this one now. So, it's going to be this one. It's going to have this as the description. And then it's going to mulligan on this one. You saw nothing. Uh, and there's no reason to believe this would fail now, right? We can just merge this. We can just merge this. We can just merge this. It's fine. And I will choose not to add a bunch of details to this. No, I'll add it. Fine. [sighs] Fine. Confirm. It'll just be the sever check. Yeah, it's fine. So, as I was saying, now that we've merged this PR with no problems whatsoever, we can go back to main. Uh, we can delete the erroneous tag I made. Um, and now we can we can tag and then we can cargo publish and get push tags. And now let's see how this does. Oh, this is cued because I've probably been running too many CI jobs. But see now dependabot is starting to close its uh close its PRs because we've already done the updates. [laughter] Well, this is the other maintainer of HDR histogram. Uh release of this last commit to this project was 3 years ago. So, not sure I'd say I have plenty of time for it, but luckily it also seems to be in a pretty stable state. Um, I'm regularly going in and gardening it either like collecting um until it makes sense to uh also doing this on stream. So, say hi >> [snorts] >> Great. So, this one's now closed. We can ignore it. Uh, this one's now merged. So, now we can merge these guys. Um, although I am going to tell the pond to rebase all of them. Aha, now they're running. Great. It's okay, John. You can swear we've grown. Uh, yeah. So, Simmer is unhappy, but that's okay. The only reason I haven't hit uh enter on this yet is just I want to I want to see the CIB green on main before I push the tag and actually upload. Um although if if Mac OS is the only one left then I'll I'll leave that to to be so dependent rebasing. This should now all turn green at least in theory. Come on. And then on these we'll also put uh released in 7.6.0. We won't post it yet because technically it's not released yet, is it? But it shall it shall be it shall be. Oh. Um, do you have your own skills in claude? I do have some claude skills. Like I have one for writing commit messages for example that I'm decently happy with. Um, I have one for technical writing. Um, I also have some that I use for uh, so if you look at AVDL for example, which we also built on stream. Um, you'll see that there's workflow prompts. Um, this is some of these are pretty good for change log for writing change logs and stuff. um and also for iterating on this particular codebase in such a way that it keeps getting better. Um so some of this was actually in the the stream we did on AVDL. I talked through how we arrived at this particular uh prompt loop. Um so I have some that are project specific and then some that are are um more broadly used like that are basically in my standard config. If you look at uh configs, don't know how many of these I've actually checked in of the skill. I don't think I've checked in any of the skills yet because I still need to do some tuning of them before they'll be useful to other people than me. But it's uh it's on my list. Um okay. I just want like one of the actual Linux test suites to complete. Don't know. Well, how is minimal versions of Windows finished before Linux has? What is this? I just want just one of you. Come on. I wonder if it's the linker. I wonder if like linking the test suite for this just happens to be really slow. I want to hit the button. Then dependabot is now happily rebasing. Uh the other thing that's nice is dependabot remember how we changed the CI so that it's now tagging commits instead of versions. Dependabot understands that format. So you'll see it it will update the version in the comment of the tag but then also actually update the commit to the thing that points to that tag. Um so that gets preserved even though dependabot updates them. Oh yeah, now we have green. We have green. Okay, now I'm fine hitting enter on this. So now this should upload. Yeah, setting up mold would probably sp speed up um CI quite a bit. It's true. At least for some cr for others it doesn't really matter, but it would also make the CI more complicated. And there are a bunch of projects for where that's not doesn't really matter. Um, okay. So, this comment can be posted. This comment can be posted. This comment can be posted. And these I can close. This I can close. This I can close. Uh, and now I suppose technically it's these where I'd be I'd be shocked if any of these actually failed CI. Um, but we will let them run through. So, we'll put those in the background. Uh, and then I wasn't going to do Inferno, but I guess we'll we'll look at Inferno real quick. Um, so first, so Inferno is the port of um the port of Flame Graph to Rust. It's being used in cargo flame graph for example to render those like flame graph looking SVG files for uh performance information. Um, one of these is adding documentation to the crate. Um the crate is not really built to be super usable for like programmatic access a little bit but it mostly has a lot of command line tools. Um so it has like actual binaries to replace the flame graph pearl binaries but it does have a slight library interface and this is trying to add um like module level documentation to the the the Rust API part of the crate. Uh, this one I've actually already replied to, I saw, which is, um, it is good to add documentation to this, but I actually want to do a pretty substantial refactor of the API to this crate. Um, so this one is an MR that I I posted back in March. That's I finally had a chance to like sit down and go over this crate and make its API what I think it should be. Um, and there's been an bunch of open issues for like, oh, can you do something like this? And finally, this is now made. Uh, and I think this will actually become the 1.0 release of Inferno, but I'm basically waiting from feedback from the people who said they wanted these features on, hey, is this what you were after? And I haven't heard back from any of them yet. Um, but you know, when that eventually comes in, then this is the way to go. Uh, and this one does uh it doesn't add too much in terms of documentation, although it does add some. Um, and so my hope is once this lands, then it'll be more useful to add actual documentation on top of um that new API rather than try to document the stuff that's already there. Uh, so this one is just a there's no no action here. So I'll mark that as done. Uh, this is uh I'm profiling programs on unfortunately Windows and using Vtune. It seems ample XCL is being deprecated. Now instructor to use vtune for the CLI. Ah [sighs] so uh let me dig this up. So this is why did I do that? Uh the stream cd in minor inferno. I really need to tidy up my uh dev directories a little. Minor inferno. Minor inferno. um get poll. So I have the latest. Yeah. So I don't think there's actually anything in here apart from documentation. Ah our support for Vune. So um this tool takes the output from various different debugging tools like Perf uh and then turns them into that flame graph visualization but you basically need to implement a a parser for each output format. So Perf produces things in one format. Vtune produces things in another format. Um Drace from Mac OS produces things in the third format. Java produces things in the fourth format. And so there's a bunch of different format supports. And so the format support for Vtune has been the format of this uh Ample XCL tool that comes with Vune. But apparently that's no longer the tool to use. And so if it depends on whether this is just Vune now ships Vune is like the perfect equivalent on Windows I guess kind of um if V2 now ships with the CLI. The question becomes whether that CLI produces call graphs in the same format as the old tool and just use this CLI instead or whether it also comes with its own format because that would be a little bit painful. Um uh yeah so let me go. Thanks for the report. Do you know or can you check um uh whether the whether Vune produces the same output format as uh what's it called? Ample amp ample XCL. I'm sure there's a good reason for that name. um or whether it also has its new output format it uses. If it's the former then yeah we should just update all the docs but if it's the latter also need to implement a whole new format given that I don't have YouTube myself. uh would probably be a lot quicker for you to just do a simple local check. Um this is also something you end up doing a lot in open source maintenance is like relying on other people because like I don't have a Windows setup that I could easily use for this kind of development task. Uh I have one for gaming but not one for this kind of stuff. And so rather than me trying to like spin up a Windows VM and then try to install Vune and then get the old tool installed and the new tools installed and then compare them, it's a lot easier to just be like reliant on the community that needs that particular support. Um, and sometimes that will also be the reason why something stagnates out like an issue or PR is like I don't I'm not in a position where I can test this or document this. Well, I need input from the people who supplied it or the people who will use it, but until I get that, I'm sort of stuck. Uh, and then you you end up people asking like, has this made any progress? And the answer is no, because the people who need it haven't told me the things I need. Uh, and so it does really become this sort of community of maintenance uh more so than like one person being the one who is sort of the the sole proprietor of the um uh of the crate. Um so hopefully this is the simple change but I guess we will find out. Um this has been closed. Ah yes. So so someone made this huge PR back in February where they made lots and lots and lots of changes to um to Inferno to try to make some documentation changes, some feature adding and some performance improvements. Uh and I basically told them I looked through some of the uh the changes and um I basically told them look you have to split this into multiple PRs. I cannot review this on its own because it's like if you look at it you know it uh you can't tell cuz it's behind my picture but it adds 314 lines removes 264 but the changes are like all over the place. Some of them are reasonable some of them are a little weird. Some of them completely change the the structure of some of the formats. Um, and so I'm like the many of these may be good ideas. I can't review them in this form. So could you please split them into multiple PRs? This is also not uncommon in open source maintenance that you basically push back not necessarily on the change but the mechanism whereby the change was delivered right so it might be it wasn't documented or it wasn't tested or can you change the code around so that the diff is minimal or in this case I can't review it in this form. I need to review it in more contained changes where I can you maybe say no to some parts of the change but yes to others. Um and so this is the kind of stuff where I basically told them I'm happy to look at these but you need to split them up. And then they split them into this. This is sort of the first change uh where I left a bunch of comments and then it seems like 5 days ago they closed the PR. I'm not quite sure why because I think this was a somewhat reasonable change. Um, in fact, most of the most of the bits in here that I comment on were more like things to tidy up, not really a push back against the change as a whole. Um, and I don't know why they closed it without any uh context. Uh, just noticed you closed this any particular reason. seemed like it was decently promising and we'll see sometimes people just vanish uh or they get tired of open source or they did it as part of work and their work situation has changed. All sorts of reasons why these things happen. Um sadly not not uncommon. Um, right. Some of these now can go away. So, for example, this one I can mark as done. This one I can mark as done. And then these we're still working on. And this can go away. And this can go away. Uh, and then we have the last inferno change will probably be the last change of the day. Uh, avoid integer overflow when computing percentage for large sample counts. Ah, I think I remember this bug report. So way back when Yeah, I think this was like someone ran a recording for a very long time, so they ended up with huge sample counts. Something that seemed entirely implausible. Uh, but then they ended up because we do some like multiplications to to scale things up to make the SVG look right. And the multiplications basically made them overflow um when turned into into integers because we do the sample count times 100. And if that overflows a U64, you get an overflow, you lose all the precision or or it crashes. Um, and so I basically told them, look, something looks suspicious, but also we could try to tweak this computation so this problem wouldn't occur. uh like for example turning into an F64 first before multiplying by 100. Um but this was back in 2020 and I guess here now someone is fixing it. Um when a stack has a very large sample count 100 times samples overflows uh performing a flo floating point over uh avoids the overflow it's unchanged from normal magnitude inputs. Uh yeah and I think I actually I already reviewed this change because it it's very straightforward and I just asked you add it to the change log. Um, and they did that last week and then three hours ago they pinged me. Uh, okay. Let's uh let's go in and take a look. Yeah. So, it just moves that to be an F64 multiplication. Same for that. And then adds a regression test and adds a fix to the change log. So, this looks great. So let's get that in uh approve workflow to run. Then do we need the individual commits here? No, I don't think so. Uh and so what are we at here? If we look at the log, ah we're just one version like the last release was not that long ago. So the the only thing in the change log is going to be this fix which seems fine, easy enough for us to land. Uh once CI is happy. Um so we will get that in there. This will just become uh 128. I I'll wait with that actually until this guy comes through. Oh, okay. is we got we got we did get four projects I guess in the end right we got um left right HDR histogram IMAP if you count it and then inferno which was not very much and this is also the nature of these things like sometimes there'll be a bunch of issues open but they're all actually pretty quick to reply to sometimes there'll be like two things but they take an afternoon to do anything about like left right because it's there's so much stuff in the details right Um for large sample counts that can go away. It just merges them anyway. Confirm squash and merge. Excellent. Get pull uh uh head cargo. So I want release of 127. Sorry, I want release a 128. Uh 128 cargo update, cargo lock. That's great. Cool. Uh and change log needs to change because this oops what did I do here? Uh so this becomes 7 to 8. This becomes eight and forward. Uh and This goes here. 128 2026 07 18 0 128. Actually, wait. There we go. That's what I meant to do. And also, there's an extra space, right? No, there's not. I lied. Uh, get commit and cargo update. Are we behind on anything? We are not. Okay, great. Uh so we'll do get push u origin head over here release 128 just to see that CI is still happy. How are these guys doing? They're still running. That's still running. That's still running. Let's go ahead and squash and merge that. And I'm also happy to squash and merge this one. Can this still beh update? Very very frustrating. So now they both have to be rebased again. So dependabot has to go do that. So we can wait on this guy. Um and this too we can uh do this hard to release 0128. Um and then we will do cargo publish and get push tags and get push. Uh once I do this so many versions. So once this is done, then we're all good and then it shall be the end. Oh, we're so close. Yeah, Windows latest is going to be fine, I think. Don't think there's really any Windows specific code in this crate at all. I don't think so. I mean, there's there's like implementation of Windows specific parsers, but they're compiled on every platform. Do I need to tell the penobot to No, it's already rebasing. Great. It's already rebasing here. Come on. Come on. And here we don't need to hit the merge button because we're going to just push the commit directly from uh from main. Excellent. And then we can go here and say released in 0128. Hooray. Done. Excellent. Delete branch. How's this now doing? Uh we are pretty close now. That can be marked as done. And now it's just these two dependabot things which these I can handle on my own. You don't need to watch me wait for CI for dependabot bumps. Uh any last things on either of these? Let's see. So, if I swap this back to newest to oldest, just those two. The rest is just random other stuff. Look at us go team. We did it. Now I got [snorts] to go actually have dinner and water. But we what we did it for about 5 hours and we got through four projects, maybe three, maybe two depending on how you count. And this is the world of open source maintenance and why sometimes it takes a while before uh people actually comment on um on the thing that you uh the the thing that you left a PR for or commented on 3 weeks ago and you still haven't gotten a reply. If you have enough projects to maintain, 3 weeks could easily go by before you have a chance to look at it again. It's not because anyone hates you. Um with that I will sign off for today. Thank you for watching and uh I'll see you next time.