I know Clojure misses the mark for you in some major areas, but having a real, proper REPL for an agent to interact with makes for an extremely strong feedback loop. IMO, a strong candidate for an "agent endgame" language has this.
I love REPL-driven development, and exploratory "programming" via small snippets is likely part of the endgame (the more the agent strays outside its comfort zone, the more it's needed). It also looks like it paradoxically saves context, since piling up many small snippets is still better than trying to fix a one-shot gone horribly wrong.
And yeah, if Clojure had a better static safety story, it would actually rank high, since it's high-abstraction with metaprogramming capabilities, excellent runtime specifications, and has a good "garden path" ie. natural code is good code.
(As a devil's advocate though, REPLs can be replaced with gluing together scripts with ad-hoc state/caching via the file system, which LLMs seem to be pretty good at already...)
So as an addendum, introspectability is also important ie. allowing to discover the state and composition of the system at any moment - though that's partially a language (eg. reflection) and partially a tooling (eg. debuggers) issue.
It sounds almost squarely written by someone without practical experience of REPL-driven development. Specifically with Lisp REPLs. Sure, other languages also have REPLs, but if you dig just a bit deeper, you'd learn that every single step there in R[ead] E[val] P[rint] L[oop] has differences. That makes the entire holistic experience of using the language drastically different. Not universally better for every domain and every case, just different.
Here's one of many practical examples I can give you - my WM on Mac is Yabai, it is hooked up to Hammerspoon, which can be scripted with Lua, which means I can use Fennel, which means I can have Lispy REPL. And from here, it is a bit difficult to explain the difference. The challenge is that the magic is invisible to people who haven't felt it.
The key insight is "the image vs the file". In most languages, your program is a description that gets turned into a running thing. The REPL is bolted on - a convenience wrapper around that same lint/compile/run/restore-the-state cycle. Python, Ruby, Lua, C#, etc. REPLs work that way. You're still fundamentally working with files that produce processes.
In a Lisp, the running system is the environment. There's no gap between "the code" and "the live thing". When I connect to Hammerspoon's Lua runtime via Fennel, I'm not sending scripts to a subprocess - I'm reaching into a living system and reshaping it while it runs.
The missing vocabulary here is "liveness", not "fast feedback" - that's a pale shadow of it. Liveness means the environment has no opinion about what's "done" versus "in progress". Everything is always mid-flight and accessible.
So I can actually reach out in live REPL session to let's say Slack app window and extract the data about every single element in the app, get the content, compare and continuously reiterate, without having to restart anything, without even saving the code - just pure data extraction without compiling, dealing with state changes, etc. I can interactively move the window, resize it, hide, or maximize it - all that programmatically. Imagine DevTools on steroids, only it works for everything, not just web apps.
Learning Lisp and Clojure allowed me to truly experience the genuine joy of programming, because it makes it feel like you're playing a video game. And now, can you even imagine what happens when you open up access to all that awesomeness and grant it to an LLM? Most people have zero idea what it feels and looks like, when you can point an agent to a REPL running in a k8s cluster and it introspects things on the fly, while you let another agent poke through the UI and they work as a team to fix something or develop a new feature.
> if Clojure had a better static safety story, it would actually rank high
This framing reveals an assumption - the primary value of a type system is catching errors early, and that Clojure is just a dynamically typed language that would be improved by adding that. In a Lisp image, "early" and "late" barely exist as meaningful categories. The feedback loop isn't compile-time vs runtime - it's just... now. You evaluate a form and you know immediately. The error is right there, in context, with the live data that caused it.
Static types are, in a real sense, a compensation for the gap I just described - the gap between the description and the running thing. When you can't easily inspect or reshape the live system, you want the compiler to tell you as much as possible before you cross that gap.
Clojure has Spec (which can do things most other type systems would struggle to express), it has instrumentation, it has a rich data inspection story. You said "debugger", Clojure has Flowstorm, which is one of the best debugger experiences I have ever encountered in any language, and I have used more than a few.
> Static types are, in a real sense, a compensation for the gap I just described - the gap between the description and the running thing. When you can't easily inspect or reshape the live system, you want the compiler to tell you as much as possible before you cross that gap.
I think this underrates static typing. For me the biggest value add of a static type system is that while doing a big, breaking-change refactor, I can near-instantly see all the places I need to update callers. Getting the code to work in the place I was actually working on is easy, I was already focused there. Static types pay off by helping me know when my change broke other parts of the system I wasn't even thinking about.
I'm not underrating static typing and I'm not saying static types have no value for refactoring - they absolutely do. But the "I can't refactor safely without types" argument tends to assume a codebase structured the way typed codebases are structured. Idiomatic Clojure has a different shape, and the refactor pain you're describing almost never materializes.
What the static typing camp can't acknowledge is that not all dynamically typed languages are equal. I agree with your sentiment e.g., on Python codebases - it's legit pain, but PLs like Clojure and Elixir are a different story.
Data-orientation flattens the call graph. Most Clojure functions take and return plain maps/vectors/seqs. A "breaking change" to a data shape doesn't ripple through type signatures across the codebase the way it does when every layer has its own nominal type. You change the shape at the edges (parsing, validation via Spec/Malli) and most intermediate code keeps working.
Fewer, more general functions. map, filter, reduce, get-in, update, etc. replace dozens of bespoke typed methods. There's just less surface area to break. But then again, I would say `(map)`, and someone familiar with Javascript's `map()` would have some wrong assumptions.
You really just can't evaluate ANY language by picking a single aspect of it without wholly understanding the holistic picture in practical, battleground scenarios. Yes, Clojure is dynamic, but it doesn't mean it's harder to maintain or to more difficult to build with, or you just can't write robust software in it. It genuinely has qualities that shine in some domains.
My claim wasn't "static types are useless for refactoring" - it was that they're a compensation for the gap between description and running system. Your refactor scenario fits that exactly: you need the compiler to tell you about distant breakage because you can't cheaply ask the live system "who calls this, and does it still make sense?"
In a live image, that question is answerable directly. find-usages, instrument the function, exercise the path, watch what flows through. The "places I wasn't thinking about" announce themselves the moment they're touched, with real data in hand - not as a list of locations I now have to go read and reason about cold.
Your intuition is not right, I have plenty of experience with REPL-driven development, even having used all the tools you have mentioned in your post. (and Clojure Spec is exactly the runtime specification part I was referring to in my comment...)
Logically, it's more the other way around - REPLs are in that sense a compensation for static types, since nothing forbids having a REPL and static types (see: eg. Julia. OCaml, Haskell, Scala etc. have REPLs too, but they are closed-world).
There's a fundamental trade-off though, with the open- vs closed-world assumption - if your types and (generic) methods are open world, then certain kinds of errors are just not analyzable. (this is a more general mathematical truth, also occurring in eg. description logics and the Semantic Web)
As you hinted, REPLs also don't work well for eg. large pipelines farmed out to clusters of machines, or anything that requires a high degree of static assurance, eg. control software - you either must statically analyze for all errors for it to even be legal, or the cost of having to "live fix" an error is much larger than preventing it in the first place (or just impractical for clusters).
Sure, a degree of live patching and experimenting is desirable (namedrop JPL as Lispers do), but that's a somewhat orthogonal concern.
A Lisp-style REPL combines introspection and live patching for both code and data. This can be achieved in many heterogenous ways besides a REPL, though. It's just not a nice, curated and holistic experience. But agents don't really care about that. LLMs don't get magic feelings. They can easily work around minor annoyances and a lack of ergonomics (assuming it doesn't burn context).
(Since LLMs tend to leave junk behind and slowly corrupt everything over time, a persistent image is also not necessarily a good thing. Reproducibility is also harder.)
The Unix philosophy "everything is a file" can ultimately also be viewed as image-based programming. [1] The system is the image.
As long as you have a way to address (via files, namespaced symbols, UUIDs, URLs, numbers etc.) the code, the execution state (ie. the stack frames) and memory (ie. the heap of objects), it has the same effect.
The problem with most software is that its internals are not addressable. I don't think one has to use eg. Lisp to fix that though. I do acknowledge that most static languages fail here.