origin
Provenance / origin-context layer for the persist (create-flow) system.
persist.origin
Provenance / origin-context layer for the persist (create-flow) system.
The problem persist must solve: when the agent freezes live play state into
scene.json, it must capture only the things that would not otherwise be
reproduced on the next load — i.e. creations whose stack root is the agent's
ad-hoc execute() context. Anything created by onLoad / the scene loader or by
a component callback re-runs on every load, so freezing it would duplicate it.
This layer tags each live creation with its origin context (via wrappers
installed over the entity API) and exposes the current origin, so the serializer
can keep execute-origin entities and drop the rest. See
docs/specs/multiplayer/candidate-c-persist-flow.md.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
conforms to
zero/source-extract/v2module persist_origin Provenance / origin-context layer for the persist (create-flow) system. THE PROBLEM persist must solve: when the agent freezes live play state into scene.json, it must capture ONLY the things that would NOT otherwise be reproduced on the next load — i.e. creations whose stack root is the agent's ad-hoc `execute()` context. Anything created by `onLoad` / the scene loader or by a component callback re-runs on every load, so freezing it would DUPLICATE it. (See docs/specs/multiplayer/candidate-c-persist-flow.md.) THIS MODULE is the foundation: it tracks the ORIGIN of the currently-running Luau context as a span stack, and stamps that origin onto every /runtime artifact at creation time so persist's scan can filter by it. origin kinds: "execute" — an agent call (the engine's authoring window is open) "scene" — scene loader / entrypoint onLoad (`__scene_load.inProgress()`) "component" — a script-component lifecycle callback (awake/update/...) "engine" — engine-driven code that is none of the above (an assetType behavior composing an avatar, a bundle exploding its hierarchy). The DEFAULT: a surface is authored only while the authoring window is open, so a surface added later classifies as code without being enumerated anywhere. Spans are pushed by the engine's existing entry points (scene_loader brackets load with `__scene_load.begin/finish`; the component dispatcher wraps each callback) and the TOP of the stack is the current origin. `execute` needs no explicit span — it is the absence of any other span. Tagging: the origin is written as the `__origin` entity attribute (and an id->origin side cache). The attribute is `_`-prefixed, so the existing scene_saver serializer (which drops `_`-prefixed component-data keys) strips it automatically — it never bakes into scene.json, yet it is inspectable on the live /runtime entity exactly as the create-flow design requires. Runtime ASSETS (textures/meshes/materials forked under /runtime/assets) carry the same origin in their `.metadata` via `tagAsset`.
stack( ) → void
push(kind: string, meta: any?) → void
Push an origin span.
| arg | type | description |
|---|---|---|
| kind | string | |
| meta | any? |
pop( ) → void
with(kind: string, fn: (...any) → void
Run `fn` inside a `kind` span, popping even on error. Returns fn's results.
| arg | type | description |
|---|---|---|
| kind | string | |
| fn | (...any |
current( ) → string
The current origin: explicit top-of-stack span wins; else infer scene-load from `__scene_load.inProgress()`; else the agent's `execute` context.
cache( ) → void
attrSetter( ) → any
tagEntity(id: string, kind: string?) → void
Stamp `id` with the current origin (or an explicit `kind`). Idempotent.
| arg | type | description |
|---|---|---|
| id | string | |
| kind | string? |
entityOrigin(id: string) → string
Read an entity's origin: cache first, then the durable attribute, then nil (untagged == created before tagging / by a path we don't wrap == treat as non-execute by callers).
| arg | type | description |
|---|---|---|
| id | string |
isReproducedContext(origin: string?) → boolean
The reproduced-by-code contexts: an entity created here is re-created on the next load — by the entrypoint / scene construction, by a component callback, or by an engine-driven path (an assetType behavior composing an avatar, a bundle exploding its hierarchy) — so persist must NOT freeze it, else it duplicates.
| arg | type | description |
|---|---|---|
| origin | string? |
shouldFreeze(id: string) → boolean
True iff persist should freeze this entity (gate by CREATION context). Saves everything EXCEPT reproduced-context creations. Untagged (execute, or any creation path we don't wrap) => saved. "What you see is what you get."
| arg | type | description |
|---|---|---|
| id | string |
componentOrigin(entityId: string, componentType: string) → string
An entity's origin answers "who created this entity". A component instance carries its own answer: an entity the author placed can pick up components that CODE attached to it — a camera behavior a `Camera.behavior` write reconciles, the character-controller expansion an avatar bundle materializes on its host. Those re-attach from the same source on every load, so the authored scene record leaves them out; the engine records the attach context and reports it here. The recorded attach context of one component instance, or nil when an authoring path (the agent's `execute`, an editor tool) attached it.
| arg | type | description |
|---|---|---|
| entityId | string | |
| componentType | string |
componentDrivenFields(entityId: string) → void
The component fields on this entity whose live value a component callback wrote while the engine was in play, keyed by resolved component type name. A lamp component writing `Light.intensity` every frame puts `intensity` under `Light`; an authoring call writing that same field takes it back off. The values name the running simulation's state, which the play to edit flip restores from the scene, so a comparison of authored intent leaves them out.
| arg | type | description |
|---|---|---|
| entityId | string |
shouldFreezeComponent(entityId: string, componentType: string) → boolean
True iff this component instance belongs in the authored scene record. Code-attached instances are reproduced on load, so recording them would duplicate them; everything else — including every instance attached before the record existed — is authored. Untagged means authored, so the failure mode is a redundant record rather than a silently dropped component.
| arg | type | description |
|---|---|---|
| entityId | string | |
| componentType | string |
tagAsset(ref: any, kind: string?) → void
Loose textures / meshes / materials forked under /runtime carry their origin in `.metadata` so persist's asset converter can tell which ones were made by the agent (and need promotion to /source) vs reproduced on load.
| arg | type | description |
|---|---|---|
| ref | any | |
| kind | string? |
assetOrigin(ref: any) → string
| arg | type | description |
|---|---|---|
| ref | any |
installWrappers(entityNs: any) → void
Wrap the entity creation APIs so every creation is stamped with the origin of the context that created it. This drives "what you see is what you get": persist FREEZES everything by default and excludes only the provably code-reproduced (scene/onLoad- or component-spawned), so a tool/module that spawns 10 entities from the agent's execute() call still gets captured — it never silently disappears. We stamp the CURRENT origin (execute / scene / component). The freeze filter then keeps execute + untagged (the agent's work, via any path) and drops only fresh-id scene/component creations. Tagging is via `tagEntity` (cache + best-effort `__origin` attribute, `_`-prefixed so it never bakes into scene.json). A spawn that RESTORES an explicit id replays a record a store already holds, so it keeps that record's provenance rather than the replaying context's. Idempotent + fully guarded: a wrap failure (e.g. a sealed namespace) degrades to "no auto-tag for that fn" and never breaks engine boot.
| arg | type | description |
|---|---|---|
| entityNs | any |
tagCurrent(id: any) → void
| arg | type | description |
|---|---|---|
| id | any |
idOf(value: any) → string
Read the id off a creation result. The creation calls answer in two shapes: `spawn` / `spawnSynced` hand back an entity PROXY, which keeps its id in a raw `__id` slot, and `duplicate` / `batchSpawn` / `instantiate` hand back the id string(s). One reader covers both, so a call that changes which shape it answers with stays tagged.
| arg | type | description |
|---|---|---|
| value | any |
restoresId(a: any, b: any) → boolean
True when this spawn call restores an id: `entity.spawn(name, opts)` / `entity.spawn(opts)` with `opts.id` set. The id then belongs to a record some store already holds — the scene body the loader is replaying, a relay snapshot — so the record's own provenance stands and the context replaying it leaves the stamp alone.
| arg | type | description |
|---|---|---|
| a | any | |
| b | any |
wrapSpawn(name: string) → void
| arg | type | description |
|---|---|---|
| name | string |
wrapId(name: string) → void
| arg | type | description |
|---|---|---|
| name | string |
wrapIds(name: string) → void
| arg | type | description |
|---|---|---|
| name | string |
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
agent_score is exposed.+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
Scoped to this part · feeds back into the world's score.