>> Right now this seems to have all the downsides of programming exclusively with "magic strings", and I haven't been able to find any cure for it or even seen this problem discussed elsewhere.

The cure is to not try to program with "magic strings". You don't need to, and if you really want to, then you should try to understand what exactly it is that you're doing, and do it right.

Specifically, what you call "magic strings" are what we call "functions" in First Order Logic, and that Prolog calls compound terms, like carring_rock(robot(R)) which is in fact two compound terms, nested. Prolog lets you nest compound terms infinitely and if you really want to hurt yourself, one great way to do it is to nest terms everywhere.

The alternative is to understand that when you use compound terms as arguments to predicates (or other terms) you are really _typing_ those arguments. Above, "carring_rock(_)" is the type of the second argument of result/3, and "robot(_)" is the type of the single argument of carring_rock/1. The catch is, of course, that Prolog is not a typed language and it doesn't care if you want to hurt yourself. So if you need to have types like that, then you should write some code to explicitly handle types and do some type checking. For instance, right out the top of my head:

result_type(T):-
T =.. [carring_rock,R]
,robot_type(R)
,!.
result_type(T):-
throw('Unknwon result type':T)

robot_type(T):-
T =.. [robot,R]
, % ... further typing of R
,!.
robot_type(T):-
throw('Unknown robot type':T)

Note that this is not the right way to throw errors but I can't now.

A simpler thing I'd advise, but that's going into style territory, is to keep variable names as short as possible. One reason it's hard to spot the mistake in the code above is that the source code is all cluttered with long variable names and the nesting of compound terms makes it even more cluttered. An IDE with good syntax highlighting can also help. Perversly, I find that there are many people who code in Prolog either without syntax highlighting at all or in IDEs that are not aware of common results of typos, like singleton variables. The SWI-Prolog IDE is good for this and Sweep for Emacs has a good reputation also (although I haven't tried it):

https://eshelyaron.com/sweep.html

Edit: it just occurred to me that the project I'm working on currently, in my post-doc, involves quite a bit of typing using compound terms as arguments, like you do above. I've opted for a program synthesis approach where the predicates I need with typing are automatically generated from a specification where I define the arguments of predicates' types. Doing the same thing by hand is probably my number two recommendation of how not to code in Prolog. Number one is "the dynamic database is evil", but does anyone listen to me? Never.

Edit 2: Covington et al's Coding Guidelines for Prolog make the same point:

5.13 Develop your own ad hoc run-time type and mode checking system.

Many problems during development (especially if the program is large and/or there
are several developers involved) are caused by passing incorrect arguments. Even
if the documentation is there to explain, for each predicate, which arguments are
expected on entry and on successful exit, they can be, and all too often they are,
overlooked or ignored. Moreover, when a “wrong” argument is passed, erratic be-
havior can manifest itself far from where the mistake was made (and of course,
following Murphy’s laws, at the most inconvenient time).

In order to significantly mitigate such problems, do take the time to write your
own predicates for checking the legality of arguments on entry to and on exit from
your procedures. In the production version, the goals you added for these checks
can be compiled away using goal_expansion/2.

https://arxiv.org/abs/0911.2899

>> I could linearize the arguments and include them in the body but this destroys the indexing and can put me into "defaulty representation" territory.

Sorry, I don't know what either of those are: "linearize the arguments" and "defaulty representation".

Prolog only has first-argument indexing normally, although some implementations let you change that and use your own indexing scheme. Is that what you did?