The obvious answer is that the more bugs you batch up, the higher the chances the next build fails - this is why CI became a thing, small iterative changes are safer and lead to greater throughput
CI doesn't mean doing all the tests all the time though. The expensive tests still wait until there's a major reason to run them. I had the same question as the parent and I still don't quite see why this can't work.
I didn't work on this project, but I've been involved with similar ones.
There is a process for getting a change into version control. Each change needs to have a (virtual) paper trail: motivation, risk analysis, sign-offs &c.
If you can't get something into VC quickly, you can't really do CI.
The obvious solution would be to have an integration branch that doesn't need the process to get in, do CI testing on that branch and then make the process for merging to the real branch.
I've never seen this done personally, but I have been told some places do it, and then you end up with "Change X, which got approved had a dependency on Change Y that didn't get approved and we didn't realize it until now because Change Y was put in the integration branch before Change X"
That sounds odd. The way it was done in every place I worked, is that a set of changes were approved for a release before they were planned and implemented. We organized the work as expected: each bug/feature on its own branch, with its own set of unit tests, etc., and automated testing applied on each commit. These branches are then merged to the integration branch once they are known good. Before the release process starts, QA would get a copy of the integration branch and test that.
The dependency problem doesn't exist, because all the features were already approved to be in the release. The only way there would be a problem is if someone decided late in the game to pull a feature and that feature was a dependency to something else.
What I described was for bugfixes, not features. Features were set in stone way before this.
I also maintain that it is impossible to know which changes depend on other changes. In one case, applying a bugfix that changed the order of allocations at startup caused vtable corruption somewhere else because it changed how much padding a particular malloc() call was returning, and someone was writing past the end of their allocation.
[edit]
Also note that what you described is not CI; things are developed on their own branches and not integrated immediately.
It's may seem like a nitpick but I think it's a whole different way off looking at it to phrase it as "applying a bugfix that changed the order of allocations at startup exposed memory corruption somewhere else"
I do think there is something to be said for both perspectives, especially for code that is extremely critical. With sufficient testing and determinism maybe you can actually make sure that dormant issues stay dormant meaning there is real value in being change-averse. Still it's a very precarious situation having a known memory corruption hoping it's the testing has made sure its benign in practice.
IMO All it exposed was writing past the end of buffers.
There was no memory corruption previously due to partially to luck, and partially to heavy testing (which would have exposed most forms of memory corruption).
I think it is fare to say that the change was a cause of the vtable corruption occurring, since without the change it didn't happen.
Once this is discovered, you need to rethink your change plan for the next release; if you back out this one change, your software will return to a working state. Whether doing that, or fixing your buffer overflow is the correct thing to do depends on a lot of specific factors.
Well, approval is a different beast from passing tests. But also, that's not how I was imagining this. I was imagining maintaining separate branches on top of each release, only combining them (merge or rebase or whatever) when you have a good reason to. That keeps things independent and makes it so you can always cut a release with solely the critical fixes (and test them in isolation, etc.) whenever needed, letting you integrate the noncritical ones opportunistically.