In the story of how ST/ORM came to be, we kept saying that an entity is a plain record. It sounds almost too simple to matter. It is the decision the rest of the design leans on.
A record, or an immutable Kotlin data class, is a value. It has no object identity beyond the fields it holds, and two of them with the same contents are equal. You can copy one, put it in a set, hand it to another thread, or pass it to a serializer, and none of that is risky, because there is nothing hidden to disturb and no lifecycle to violate. What you read in the source is what exists at runtime. An entity in ST/ORM is exactly this, and nothing more. It is the same plain shape we followed to its conclusion in a first-principles look at the data layer.
A managed entity comes with a vocabulary. Attached and detached. The persistence context. Flush, merge, cascade. None of those words describe your data. They describe the machinery that tracks a mutable object across a session so the framework can work out what changed and when to write it. When the entity is a value, most of that machinery has nowhere to attach itself, and the words quietly leave your codebase. You are left reasoning about data, not about a lifecycle.
Because the entity carries no session and no persistence state, you can pass it anywhere without a second thought: out of the repository, through your services, into a controller, across a coroutine boundary, and back. A service can return an entity without also returning an invisible dependency on an open session. There is no session for it to outlive, so the error that haunts a managed model, the one that fires after the session closes, cannot happen here. You can cache it, because it will not change under you. You can serialize it, because it is only its fields.
Two worries usually surface here, and the answer to both is: nothing you would miss. Immutability does not cost you dirty checking. You change an entity by producing a new copy, and ST/ORM writes only the columns that differ, by comparing the value you saved against the one you read, with no proxy involved. And it does not cost you lazy loading. When deferring a load is the right call, you write the field as a Ref, and loading it is a call you make, in code a reviewer can see. Both are still here. The one thing that changes is that they are explicit instead of hidden, and that is not a price you pay for plain values. It is much of the reason to want them.
That is the quiet bet behind ST/ORM. The database owns the data model. The application gets plain, typed values it can pass around, copy, test, cache, and serialize without asking what session they belong to. Once an entity is just data, the framework can do less, the code can say more, and the value can be trusted everywhere it goes.