> But there's no formal concept of "interface" in Prolog

Any predicate you write for someone else has an interface. Someone else includes you in a couple of weeks. All predicates that are not truly auxiliary ones which are more like basic blocks of command oriented programming languages - COPLs.

> instantiations errors are a possible issue at every level of a program

That is not the worst that can happen. Nor is non-termination the worst. The worst is if an unexpected failure or success happens that
looks like a legitimate answer. However, for many, non-termination is seen as being worse than such real errors. The reason is that there
are only very few Prolog systems that reliably catch resource errors or have reliable timeouts. Well, to be true, there is one system. And the others most often abort.

> (hence my practice of documenting the structure of inputs and outputs carefully).

> I really don't like the ad-hoc "type" checking in Prolog (as in integer/1 etc).

integer/1 is one of the orignal sins of DEC10 which remplaced the errors of Prolog I by silent failure. And everybody then followed suit. Mindlessly. There is library(si) that offers integer_si/1 to make things much cleaner.

As for your remarks on type systems for Prolog, one feature they do not provide is a dynamic conversion between regular Prolog and the typed part. There was an attempt some time ago but unfortunately the individual left for FP.

> Who needs type safety anyway?

It seems there are two different objectives: type safety and instantiation safety. Both are often confounded.

> SWI-Prolog's Picat-style matching with the "=>" operator that is an
> alternative to ":-".

This approach does not distinguish between instantiation and type
errors. Take the sum_list/2 from
https://www.swi-prolog.org/pldoc/man?section=ssu

?- sum_list(1, N).
existence_error(matching_rule, sum_list(1, 0, _A)).
?- sum_list(L, N).
existence_error(matching_rule, sum_list(_A, 0, _B)).

And then when adding the suggested catchall rule, both fail. Both. This throws us back by almost half a century. So now DEC10-style
errors get into the 21st century. Prolog 0 and Prolog I were better.

?- sum_list(1, N).
false.
?- sum_list(L, N).
false, unexpected.

> "steadfastness", that you also mentioned, and that is a new term for me

Since 1987-09-29 in common use, see the Prolog Digest of that date, posting by Richard O'Keefe. Here is my definition: an argument Ai is steadfast w.r.t. a goal g(A1,..Ai,..AN), when executing this goal is equivalent (complete operationally) to, g(A1,..CAi,..AN), CAi = Ai with CAi a fresh new variable. And more generally an argument Ai is steadfast, if this property holds for all goals.

A good example of a steadfast argument is the second argument of append/3. You can replace any goal append(Xs, Ys, Zs) by append(Xs, CYs, Zs), CYs = Ys without observable effect (except efficiency, resource consumption and the like). But even non-termination is preserved! Another one is the 3rd argument of phrase/3.

Or take your run_length_encoding/2/3, where the second argument is steadfast. You (presumably on purpose) accumulated in the second argument all the elements only to reverse them finally in a highly lispeling manner. At least no destructive nreverse.