On the for each you request an exclusive-lock. You can only acquire an exclusive-lock within a transaction. That is the very reason why the transaction function function will return true. This is done by the compiler and does not happen at runtime. Furthermore, in this case, every iteration of the for each is an individual transaction. Since you don't reveal what happens in the for each we don't know. But I do speculate that it isn't much and therefore, depending on the size of the data set, you have a lot of transactions with an extreme short duration on the database.
Therefore - somebody correct me please if I am wrong - IMHO it is pure luck, when you query the _Trans VST that it shows an active transaction. Or in other words, due to timing it would be hard to prove you point.
The rest is buffer and, more prominently, transaction scope. The scope of the transaction will be scoped to the next outer block with transaction scoping capabilities. Since a for block does have the transaction capabilities, the transaction is scoped to that block.
Very often bad transaction scoping comes from developers not understanding that an exclusive-lock can only be acquired within a transaction and that an innocent looking find ... exclusive-lock at the procedure block level will scope the transaction - which is caused by the find ... exclusive-lock - to the procedure itself. Depending on what is going on in the rest of the procedure, you might wind up with a monster transaction bringing the database down when the before image is exhausted.
Understand buffer and transaction scope is essential to developing applications in the ABL. More importantly, for static database references, if you don't take control - the compiler certainly will. Since dynamic database references are resolved at rumtime you might wind up hitting runtime errors if you don't take control.
Please don't try to prove a point with half-revealed demo code. You should be able to prove your point with a real world code example.