Before you ship
Use stable identities
Declare capabilities truthfully
Declare the weakest identity scope you emit
session is honest. persistent you cannot back is a silent wrong match.Return deterministic records
Return everything, or error()
Refuse in phase 1, with a code and a reason
Read values back before reporting success
Return a NEW identity from Materialize
Validate the host system properly
Run the contract
vetra_blueprints_adapter test <your id> from any console, then
vetra_blueprints_adapter test <your id> full from the server console of a development
server. TestingRestart the server and check your ids
The eight that do real damage
1. Using an entity index as a persistent identity
session unless you genuinely have one.
If your domain is entity-backed, Vetra.Blueprints.Entities.Identity(ent) handles both cases
for you.
2. Deriving identity from position, model, or any property
3. Storing a Vector, Angle or Entity in a record
Vector makes the file unwritable; an Entity makes the record a
lie the moment it is removed.
Do: plain arrays of finite numbers.
4. Claiming a capability you cannot honour for every object
properties = true when most records work and some do not is not a partial capability, it is a
false one. Blueprints builds all-or-nothing group materialization and rollback-on-failure on
the promise, not on the average.
Do: declare less, and refuse the impossible cases in phase 1 with both a code and a
non-empty reason.
5. Silently omitting objects from Collect
error(). A capture that failed is recoverable; a capture that
lied is not.
6. Reaching for a field that is not on the record you were given
Collect returns and what Blueprints hands back are different shapes. This only
bites on a path that runs during a real migration or restore, which is why it survives casual
testing. Records
The same class of mistake: indexing transform.pos in an adapter whose records have no
transform. The argument is nil there too.
7. Mutating a record Blueprints handed you
source argument is a defensive copy of a Version’s record, and it is read-only.
The same goes for your own descriptor: it is copied at registration, so self inside a
capability function is Blueprints’ copy. Keep mutable state in your own upvalues.
8. Assuming load order, or registering outside the hook
Vetra.Blueprints.RegisterAdapters, as Registry shows.
Registration outside that window is refused, and the refusal tells you this.
Smaller ones, worth a look
Renaming your adapter id
Renaming your adapter id
name field instead; nothing matches on it.An expensive dependency.Available
An expensive dependency.Available
Reusing the source identity in Materialize
Reusing the source identity in Materialize
localId.Declaring orientation = "full" when you only write yaw
Declaring orientation = "full" when you only write yaw
"yaw", or
"none" if there is no facing at all.And if you declare properties, return it from PrepareProperties too, because that is
the one verification actually asks.Parking a helper or a config table on the descriptor
Parking a helper or a config table on the descriptor
self.MyHelper is nil. Registration warns about it; use upvalues.A hyphen in your adapter id
A hyphen in your adapter id
acme-games.turrets is refused. The pattern is [a-z0-9_] on each side of one dot.Assuming your preview model exists on the client
Assuming your preview model exists on the client
Shipping a client-side half
Shipping a client-side half