Your base case has a free variable, so it doesn't change whatever is passed to it. Since your recursive case never binds a value to Stops, it's always a free variable. Hence Y is just Y and isn't displayed.
The answer to your actual question is at the end, but before that I show the easier ways to do it.
Easy solution without tail-call optimization
This starts with an empty list at the base case and builds it up on the return rather than pushing a growing list till the base-case. This results in the order being reversed. You could do an append to get the order right.
% was connection(Start, Destination, TDeparture, TArrival, _)
connection(Start, Destination, TDeparture, TArrival, []) :-
train(Start, Destination, Departure, TArrival), Departure > TDeparture.
% was connection(Start, Destination, TDeparture, TArrival, Stops)
connection(Start, Destination, TDeparture, TArrival, [Stop | Stops]) :-
station(Start),
station(Destination),
station(Stop),
train(Start, Stop, Departure, Arrival),
Departure > TDeparture,
% was connection(Stop, Destination, Arrival, TArrival, [Stop|Stops])
connection(Stop, Destination, Arrival, TArrival, Stops).
Tail-call optimization using an extra accumulator
Easiest way to achieve tail-call optimization is with a separate accumulator argument. (Or I hope it achieves it). The order is wrong again here, but you could still fix it with an append/3.
% Query with connection(a, d, 8.00, X, [], Y).
% Or define wrapper
% connection(a, d, 8.00, X, Y):- connection(a, d, 8.00, X, [], Y).
connection(Start, Destination, TDeparture, TArrival, StopsSoFar, StopsSoFar) :-
train(Start, Destination, Departure, TArrival), Departure > TDeparture.
connection(Start, Destination, TDeparture, TArrival, StopsSoFar, FinalStops) :-
station(Start),
station(Destination),
station(Stop),
train(Start, Stop, Departure, Arrival),
Departure > TDeparture,
connection(Stop, Destination, Arrival, TArrival, [Stop | StopsSoFar], FinalStops).
% If you want it in the right order,
% append(Stop, StopsSoFar, NextStopsSoFar),
% connection(Stop, Destination, Arrival, TArrival, NextStopsSoFar, FinalStops).
The actual answer
If I'm allowed to guess what you're trying to do, You want to pass down a free tail so that it can keep growing till the base-case( [a,b,c, ...|T] )
The difficulty with this is figuring out a way to efficiently separate the head from T and modify T ( T=[d|U] ). Here's my attempt at that with safe_split/3. I've not put too much thought into it.
connection(Start, Destination, TDeparture, TArrival, Stops) :-
safe_split(_,T, Stops), T= [],
train(Start, Destination, Departure, TArrival), Departure > TDeparture.
connection(Start, Destination, TDeparture, TArrival, Stops) :-
station(Start),
station(Destination),
station(Stop),
train(Start, Stop, Departure, Arrival),
Departure > TDeparture,
safe_split(_,T, Stops),
T = [Stop|_],
connection(Stop, Destination, Arrival, TArrival, Stops).
safe_split(H,T,Stops):-
append(H,T,Stops), var(T), % lots of backtracking because generate & test.
!. % Prevent free variables appended to the head.
Appendix:
To demonstrate what happens without the cut
?- L=[a,b,c|_], append(X,Y,L), var(Y).
L = [a, b, c|Y],
X = [a, b, c] ;
L = [a, b, c, _5368|Y],
X = [a, b, c, _5368] ;
L = [a, b, c, _5368, _5380|Y],
X = [a, b, c, _5368, _5380]
% ... and so on.
Thanks for the fun OP, Sorry if I threw too much at you :)