To clarify, in the context of the article, "making errors observable from the outside" is not meant at the per-function level, but meant at the level of the whole program, which includes abort(). I should improve the wording there.
abort() is perfectly legitimate if you can't recover within the same process. It's not at odds with routing to the right stakeholder, as long as the parent process catches the crash and does appropriate error handling (e.g. "Send a crash report" dialogs or other watchdogs).
Regarding textual logging, I'm not sure exactly which part you disagree with. :) I'm aware that people commonly write regex-based extractors, and I've done the same, but having the choice I'd always go for a more schema-ful error reporting channel. That's more lightweight both for reading and for writing it, and avoids a whole class of regex bugs.
Regarding textual logging, I think it works wonderfully and I don't think it's at odds with schema-ful reporting. One good way is to include error codes. Informal messages are at least as important. They are ergonomic to humans and their meanings are easy to look up with a web search.
Of course textual logging can always be supported by other means, like crash dumps.
I'd beg to disagree.
As for the general statement of your post, I like how you're talking about stakeholders and (specification) bounds but I don't think it's an actionable viewpoint. The idea of "Bubbling up errors" inside a program is caught in the software reuse mindset but it's not possible to take action on encountering a true error (here "true" means "outside specifiation"). That's because the state of your whole program is undefined after the encounter. Which means that the best you can do is to call abort() and hope that the program will end with some info for debugging.
(Unless we are talking about a VM or sandboxed code, in which case again you could just call some equivalent of abort() from the code inside and "catch" that in the host code and restart the VM or something).