Some are hard-coded, others are decided by an RNG roll with dynamic odds. For example, the chance of Robert re-casting Necromancy after curing it goes down with each action taken between--simulating complacency. The point is that the trigger itself always activates--it's up to the boss's AI to decide if and how to act on it. Obviously the ultimate method of attack selection is quite different from a human player, but there's enough of a balance between hard-coded behavior and RNG use that it gives the
illusion you're fighting a real battler as opposed to a computerized simulation of one. Which is honestly a lot more than I can say for most of the FF bosses I've fought.
But yes, the AI can think on its feet to some extent. A great example of this is if you try to exploit zombification to do a ton of damage to Robert with Power Tonics. His usual mode of action in that case is to use a Holy Water as soon as possible to cure the status, heal himself, then blast the player with magic. But this move combo is not set in stone: If you re-zombify him immediately after Holy Water, he substitutes the healing for Desperation Slash, an instant-kill attack. This in turn can be countered by the player by having Scott Defend to endure the insta-kill, heal, and continue on.
Another example of this: In the fifth phase (after a certain... um, pivotal event), as the battle is winding down, he hits you with one final Rank 3 magic, either Hellfire or Windchill, to try to kill you with residual damage. Which one he uses is decided on who got more turns over the course of the fight. If the player had more turns than the boss, this means they are likely turn-whoring (as Robert is faster than Scott) and so casts Windchill, since Frostbite does more damage per hit than Ignite. This is information not directly available at the time the attack is decided, but the AI is able to compile it over the course of the fight by hooking the unitReady event and observing the turn patterns. Even though the behavior is hard-coded, it's quite a bit more complex than "Oh hey, the player just did that, let me do this as a countermeasure and hope it works". Decisions can be made based on anything that happened at any point in the fight, just as they can for the player. And sure, there are lots of ways you can exploit it. But there are lots of ways you can exploit a human player's assumptions as well!
Essentially I designed the AI with the assumption that the player WILL try to find exploits, and balanced the difficulty from there. This ensures that there are multiple viable strategies to win (essentially turning the exploits into full-fledged strategies), making feel like a real boss battle instead of just a glorified puzzle, while at the same time minimizing the chance of gamebreakers arising.
If it makes you feel any better though, the difficulty of the demo as posted was indeed way too high--my playtester confirmed this.
I've since made a few modifications, available on GitHub, that bring it back to being a fair fight. For example, reducing the rank of Defend and increasing the base power of counterattacks.