# MVU state source New playthrough schemas use the current interpreter. A cached factory with the exact same source can upgrade for future instances; existing sessions and history retain their original interpreter. Opening edits transfer variable content only, with the Session source retaining ownership of its schema. [中文](MVU.md) · [HTTP API](API_en.md) · [Request assembly](REQUEST_ASSEMBLY_en.md) MVU variables describe character state; they are a separate type from long-term memory. `tavernMvu` (protocol 1) owns content, history, revisions, CAS and idempotency. An optional manager uses this public service. Tavern neither depends on the manager nor copies authoritative state into its database. ## Discovery, configuration and identity Imported cards with InitVar/schema are initialization templates. `characterMvuId(characterId)` identifies the template, not editable state. Selecting the card allocates a separate `mvu:instance-*` ID from the template and the trusted DSH session's `id + createdAt`. Records include `templateId` and `instance` identity. Each new branch and reply swipe creates a different session and therefore a different state ID, current value, CAS revision and idempotency ledger. Templates are not exposed as manager current resources. Entirely new discovered templates default to **native**: their instances handle committed assistant variable updates without requiring the optional manager or an extra allow rule. Only selecting that card grants the session access; discovery never grants wildcard access or executes card scripts. Selection changes persist activation boundaries so another character's replies never replay into the instance. Returning to the same character restores that session's original instance. Source edits and selection do not reinitialize its content/schema. Parsing errors remain visible as `sourceError` and prevent execution. Missing or changed durable session identity rejects allocation and reads. Older ledgers retain stored modes, management operations, variables and history without migration or replay. Actual ownership follows the trusted Host manager registration: Tavern (native) before installation and after removal, manager (managed) while registered. `storedManagementMode` preserves the old preference without claiming current delegation. `setManagementMode` still saves this preference with explicit CAS; it does not replace manager policy or source permissions. Trace and execution use the same lifecycle. Explicit Host resources remain supported: ```js { mvu: { resources: [{ id: 'mvu:campaign', sessionIds: ['session-a', 'session-b'], initial: { stat_data: { hp: 100 } }, managementMode: 'native' }] } } ``` Explicit configuration also defaults to templates. `sessionIds:['*']` allows each session to create its own instance. `initial` may be YAML. A configured `characterId` without `initial` reads the imported card's InitVar entries and declarative schema without fetching scripts. Initialization objects merge in entry order; distinct schema declarations require explicit resolution. Parse failures never overwrite durable content. Configuration changes are not reset commands. The same state ID always has one current value; scope cannot hide different branch states under that ID. A deliberately shared manual logical entity must declare `sharing:'shared'`, using its configured ID as the state ID. `copy` creates a new manual resource with the existing access scope; it is not a branch/swipe inheritance operation. Multiple resources matching a bubble produce `MVU_AMBIGUOUS`. Normal forks freeze a concrete source instance, versionKey, revision and content hash before the official creation call, then atomically install the child instance. The child inherits the full variables/schema and verifiable read-only history without replaying ancestor commands or schema transforms. A reply swipe uses the regenerated reply's durable pre-turn checkpoint, including its opening configuration. Later parent changes cannot alter the seed. A root swipe uses a new empty session. A later swipe binds both the historical prefix coordinate `prefixEndEventId` and replaced reply coordinate `atEventId`, retains the official history prefix, and uses the target checkpoint as current without later edits. Ordinary forks still inherit the state at their cut. New instances do not inherit manager policies, grants, capabilities or operation IDs. Children preserve the source’s stored preference; current registration determines actual ownership, and missing permission while the manager is registered refuses execution. The first request waits for initialization and its checkpoint; missing evidence refuses execution. For a stopped human request without assistant body text, request-retry binds the durable user event and aborted turn. It freezes that turn’s immutable checkpoint, including initial card configuration; later manual edits do not change this baseline. First requests create a separate empty session; later requests preserve the preceding reply prefix. No reply version is fabricated for the cancelled request. New state uses `mvu-instances.json`. The old `mvu-state.json` keeps its original bytes and is exposed only as read-only resources with `MVU_MIGRATION_REQUIRED`. It cannot be edited, copied, provided to a model or used as an inheritance source. Shared history may already contain cross-branch changes and is never automatically split by session. Old bound sessions require explicit handling; fresh sessions can independently start from their template. The card sidebar's new-playthrough action creates a separate session and state instead of reusing a run whose MVU opening is initialized but has no DSH conversation. Old runs, state and history remain available. To recover confirmed data, a trusted Host caller can compose existing primitives: read the old resource with `read({id:oldResourceId,scope:{authority:'local',sessionId:oldSessionId}})`, create a new playthrough, and use `list`/`read` to confirm its separate instance ID and current revision. Explicitly select the `stat_data` to recover, then call `update` with that new ID, scope, `expectedRevision` and a new `operationId`. Do not submit the old complete envelope or old schema as the target content. This is data recovery through the target schema, which may transform or reject the content. A CAS conflict requires a new read and confirmation. It does not migrate history, schema, manager policies, grants or idempotency records, and never changes the old ledger. Entirely new discovered templates save a native preference; stored templates retain their old preference. Actual ownership for both follows the current trusted registration. Card execution and model assembly still follow their respective configuration. There is currently no automatic history migration or dedicated migration wizard. ## Host service Use `ctx.get('tavernMvu')` or optional `ctx.inject(['tavernMvu'], ...)`. Package export `pmp-dsh-tavern/mvu` exposes the service, installation, discovery, parsers and pure update functions. | Method | Contract | | --- | --- | | `list({scope,signal})` | Read authorized resources; global local management uses `{authority:'local'}` (also accepts an empty object) | | `read({id,scope,signal})` | Null if missing; record includes `id,name,type,authority,scope,content,revision,currentRevision,historical,versionKey,managementMode`, instances also include `templateId,instance,inheritedFrom?`; legacy records include `legacy,sourceError,capabilities` | | `update({id,content,expectedRevision,operationId,scope,signal})` | Current-content CAS edit; identical operation/request is idempotent, conflicting reuse rejected; source schema cannot be removed/replaced, candidate transforms once | | `copy({id,newId,scope,signal})` | Explicit new entity; existing IDs rejected | | `history({id,scope,signal,includeBefore?})` | Source versions and success/failure evidence for that session | | `facts({id,scope,signal})` | Return bounded, body-free source observations for this session; unavailable metadata never replaces state | | `setManagementMode({id,mode,expectedRevision,operationId,scope,signal})` | CAS/idempotent preference save; receipt reports actual managementMode and storedManagementMode | | `registerCommandProcessor({id,source})` | Trusted Host registration of one precommit command source; asynchronously returns `{receipt,dispose}`, not a card write API | | `registerUsage(handler,{providerId?})` | Trusted usage decision; manager identifies providerId as dsh-memory-manager, disposer revokes delegation | | `observe(listener)` | Commit/request facts, returns disposer; listeners do not participate in writes | | `validateConfig(config)` | Validate `type:'mvu-state'`, triggers and supported strategy chains | | `captureSessionSeed({sessionId,kind,atEventId,prefixEndEventId?,targetSessionId?,signal?})` | Trusted creation primitive returning an opaque frozen-source ticket, or null with no state. kind is fork / reply-swipe / request-retry; root swipes/retries require a preassigned target session ID, later swipes/retries require prefixEndEventId | | `installSessionSeed({ticket,sessionId,signal?})` | After official creation, validate child identity, parent and inherited messages; install atomically. Same ticket/target is idempotent, conflicts reject | | `checkpoint(session,turnStartEvent)` | Freeze one pre-turn baseline at durable turn/start; later steps never overwrite it | | `discover({definition,sessionId?})` | Trusted Host discovery; stable template identity, native default and explicit session grant | A trusted Host constructing `MvuService` directly must provide `captureSessionLease(sessionId)` for instance writes and seed operations, returning a synchronous validity closure. Official installation restores the specified cold session through public `resolveAgent`, then binds the session service, live object, header, event generation and digest. Missing leases deny writes/inheritance. Published Host work is awaited before entering the transaction queue. Leases and signal are checked synchronously after the last await and before atomic commit. A pending root-target receipt persistently blocks automatic initialization; an unseeded fork cannot guess its baseline from the parent current state. Only local authority is accepted. Session reads require a source access grant. Historical reads use messageId or endEventId; combined coordinates must agree. Historical `revision` identifies that snapshot; `currentRevision` is the current entity's CAS revision. Editing historical scope is rejected: explicitly read current scope before editing. Managed execution without a manager decision fails closed. Handler returns undefined for no configuration, or `{enabled,configRevision?,strategy?,reason?}`. Registration generations bind pending decisions: removal/re-registration cancels them without consuming the durable message. Supported chains: - Store: `assistant_message_committed`, `parse_mvu_update → validate_update → apply_update`. - Retrieve: `before_model_request`, `read_content → render_state_and_update_instructions → provide_to_model`. Strategies are arrays of names or `{operation}` entries. Unsupported parameters fail explicitly. The manager owns whitelist/blacklist/rule evaluation. Store event includes native coordinates, `text` and `containsMvuUpdate`; retrieve includes optional preview/turn/step. Facts are `{id,eventId,phase,sessionId?,turn?,turnKind?,requestId?,revision?,detail?}`. Phases are started/triggered/applied/skipped/failed/completed. A `state-committed` applied fact requires an actual persistent state change. `dsh-request-observed` requires recorded request assembly to match the actual DSH llm/stream request; it does not claim successful network delivery. Preview/resolution alone is not applied. Global edits omit sessionId rather than inventing one. ## Durable history and requests Only the last non-interrupted, non-tool-call assistant message of a completed turn is applied. Identity includes session, log version, creation time when available, message ID/sequence, turn/step and end event. Nested forks use the trusted inherited boundary and never replay inherited updates. Recovery checks source references against durable history; missing/changed sources block reads/injection until reconciled. Commands execute on private candidates. Ordinary schema or missing-path rejection records diagnostics and continues; interpreter budgets, invalid syntax and persistence errors are fatal and do not commit earlier candidates. Accepted candidates persist once atomically. Failures may retain unchanged content with a diagnostic receipt. Duplicate messages do not apply twice. Budgets distinguish a single state from history collections. Each complete variables object, including derived display_data, delta_data, schema and diagnostics, retains the 2 MiB and structural limits. History, checkpoints, fork seeds and transaction receipts validate each state separately rather than charging the whole collection to one state. The durable ledger still has a combined 32 MiB limit; exceeding it refuses the atomic save and preserves the previous file and revision without trimming history. Host checkpoint/ingest freezes only session identity, event coordinates, turn completion reasons, user origin and assistant text/tool/interruption markers consumed by MVU. Request assembly, provider, media and user bodies remain in DSH and are omitted from this projection. Text fingerprints and durable source auditing are unchanged. Request source `tavern.mvu/state` must be explicitly selected in an assembly preset with `role:'system', lifetime:'request'`. It emits stat_data and update instructions with resource/config/strategy version diagnostics. Native DSH messages remain authoritative; unloading does not rewrite them. ## Round tables in Tavern Trace Each Tavern Trace round offers **MVU variables and changes** below world-book triggers, collapsed by default. MVU reads and polling start only when expanded; opening Trace alone does not read that state. The variable table shows JSON pointer, actual value, JSON type and last recorded update turn. Trigger rows show event/turn, source result, before/after values and failure or skip reason. Different attempts of the same reply remain separate; idempotent replay does not claim another variable commit. Inherited snapshots are labelled explicitly and do not claim a new child trigger. The variable table shows 20 rows per page; filtering paths or switching state returns to the first page. Historical turns default to **Last recorded state this turn (read-only)**: the final recorded version for that turn, not the pre-request checkpoint. **Assistant reply update** is the version saved after that reply was committed; skipped, failed, or no-op updates may leave values unchanged. Missing history or before-value evidence stays unknown, never replaced with current content. For editable sources, the latest Trace entry defaults to **Current variables** and separately reads the current local-session record. A failed current read disables editing and never substitutes history. Each row’s **Edit** button opens a JSON editor in its actual-value cell and becomes **Save**, submitting the observed revision and a fresh operationId. The table displays the source-validated result. Selecting any historical version keeps it read-only. CAS conflicts or validation failures retain the draft; cancel, refresh current state, then edit again. Historical views and running turns cannot save. Editing does not change management mode or enable model/store/retrieve strategies. Switching session or unmounting aborts pending reads/writes. Authenticated v1 HTTP maps source primitives: `GET /mvu/resources`, `/mvu/resource`, `/mvu/history`, `/mvu/facts`, and `POST /mvu/update`, under `/pmp-dsh-tavern/api/v1`. Reads take JSON `scope` plus `id` where applicable. Current scope requires `{authority:'local',sessionId}`; only `/resource` permits historical `messageId/endEventId`. Update body is `{id,scope,content,expectedRevision,operationId}`. Legacy/error sources reject edits. These Host UI primitives are not exposed to card VMs and do not replace card execution bindings. `history({includeBefore:true})` optionally adds `beforeAvailable` and verified `before` stat_data from the recorded parent or pre-turn checkpoint. Inherited or missing provenance stays unknown; existing history calls keep their array shape. `mvu-facts.json` retains at most 2,048 body-free trigger metadata rows globally and 1 MiB. Variable/state authority remains the existing MVU ledger. Older or evicted observations do not prove that no trigger occurred. Optional fact persistence failure is shown independently and does not turn a successful state commit into failure. ## Read-only bubble binding `GET /pmp-dsh-tavern/api/v1/mvu/snapshot?scope=` uses existing Tavern authentication. Host binds `{playthroughId,sessionId,nodeId,variantId,endEventId,sessionFormatVersion?}` against durable timeline and native message evidence. Browser input cannot select arbitrary messageId. Snapshot revision stays historical; currentRevision is the current entity CAS revision. `createMvuCardBinding({client,scope,signal?,pollMs?})` asynchronously returns `{getSnapshot(),subscribe(listener),dispose()}`. Snapshot is `{version:1,status,scope,revision,variables,resourceId?}`. Variables are the whole object including stat_data/schema. Scope is immutable; polling is bounded, abort cancels initial reads, and disposed bindings reject reads. Network or HTTP 5xx read failures retain only this binding's previously verified display, mark `readState:'failed'` and `error:'MVU_READ_FAILED'`, and deny new writes until a fresh read succeeds. Access/scope rejection or malformed responses invalidate the display; no other binding supplies a fallback. Identical repeated failures do not emit repeated notifications. Rendering owns script callbacks and lifecycle. Ordinary greeting/import/streaming bubbles cannot impersonate durable coordinates. A selected character greeting uses the read-only greeting mode below; empty-session writes use the separate initial mode. ## Downloaded, enabled card variables Imported inline scripts follow their saved enabled/disabled state and the master switch, which defaults on when unset. External code must first be downloaded through the existing import prompt or appearance panel. Once available and enabled, a supported card can read and write its current bound variables. There is no code-review step, separate write approval or permission expiry. The trusted renderer builds the complete execution bundle and obtains an internal binding through the existing `rendering-write-grants` transport with `{source,sourceIdentity,executionId,downloaded:true,enabled:true}`. Host recomputes `{version:1,sha256,scope}` and retains only that identity/scope. `tavernRenderingAuthority.resolve({grantId,sourceIdentity})` and synchronous `isCurrent` verify that the same execution is still active. Opaque tokens never enter card code. Disabling, removing or rebuilding a runtime revokes its old binding. A rebuilt runtime uses persisted downloaded bytes and saved switches to acquire a fresh binding automatically; no state or token is transferred to another session. After Host restart, durable message bindings can be recreated when still current. An opening view carrying a selection token must be reread and rebuilt; the old token is never automatically substituted or revived. `POST /pmp-dsh-tavern/api/v1/mvu/card-binding` accepts `{scope,grantId,sourceIdentity,bindingId?}` only for the active timeline head or verified empty-session opening. `POST .../card-write` accepts `{capability,operation:'patch'|'replace',value,expectedRevision,operationId,cause}`; `POST .../card-binding/revoke` stops the binding. Scope, current selection/membership, source schema, CAS, idempotency, cancellation and final synchronous execution checks still apply. Historic/running messages remain read-only. Capabilities have no time-based approval expiry and are removed when execution ends or the service unloads. The trusted client generates executionId/bindingId before creation. Identical replay allocates once and conflicting reuse rejects. Lost responses or initial failures remain revocable by the known identity; in-flight revocation is checked after the final await. Failed cleanup is visible and retryable in the current page. Automatic recovery of an in-memory cleanup queue lost through a client crash or refresh is not promised; Host unload clears its bindings. `card_variable_update` is a source execution fact, not a manager store/retrieve permission request. Missing/default manager configuration cannot block this enabled card operation. Manager continues to observe started/triggered/applied/completed/skipped/failed facts; only an actual state change emits applied with `detail:'state-committed'`, and these native facts use `configRevision:null`. Model request injection and assistant update strategies retain their existing manager policy and leases. Existing card-update policy declarations do not grant or deny native card execution. The trusted dispatcher derives cause (`user-interaction`, `interval`, `script`) from native event/timer tasks; card code supplies only operation/value and constrained options. Host trusts authenticated UI evidence, not a cryptographic proof of a human click. The renderer preserves the revision seen by the Worker and its operation ID during automatic transport recovery; no retry invents a newer CAS revision or operation ID. Observed signatures remain `Mvu.getMvuData(options?)` returning whole variables, `Mvu.updateVariablesWith(JSONPatchArray)` and `await Mvu.replaceMvuData(variables,options?)`. Callback updater overloads are not claimed. VARIABLE_UPDATE_ENDED supplies a no-argument callback when this binding observes a newer revision or recovers from an unavailable snapshot; recovery does not imply a new commit. Cross-scope fallbacks remain rejected. Network, parent-page DOM and Host tools are not added by downloading code. ## Read-only opening variable snapshot A new playthrough first reads the character's shared `[initvar]` and source schema, then applies explicit `` / `` / `` update envelopes in the selected greeting. Only supported literal update commands are parsed; display scripts are not executed. Invalid paths or schema violations reject opening initialization instead of silently skipping changes. Opening updates run once and do not create a DSH turn. Before first send, each greeting keeps its own variables. Switching restores that greeting's initial state or saved edits, without accumulating the previous greeting's updates; these edits survive refresh. Manual edits override greeting defaults. Reloading initial variables regenerates the selected opening; a changed character source also invalidates other cached openings. First send transfers only the selected opening, which becomes the first pre-turn snapshot. Started playthroughs are not retroactively initialized and real request history is unchanged. Older unsent drafts retain their current variables; reload initial variables to load the matching greeting defaults. The same snapshot API accepts `{mode:'greeting',playthroughId,sessionId,characterId,sessionFormatVersion?}` to read the selected character resource's opening configuration before the first send. Once the first turn starts, it reads the persisted first pre-turn checkpoint, including the user's opening edits. Host verifies membership as the playthrough root or a saved reply session in the durable timeline, character selection, session identity and one active resource. It does not resume an Agent, replay events or reset state. This view remains readable after a user starts a turn, including before any assistant message completes. Later replies and variable edits do not change this opening snapshot. A missing first checkpoint is unavailable; it never falls back to the latest variables. It does not follow another session's focus and cannot create a write capability. Imported or missing local sessions cannot fabricate a binding. After a swipe switches to a saved branch, the greeting reads that branch's own first pre-turn checkpoint, rather than the latest state of the root or another branch. Historical replies continue to use their exact durable message coordinates; a later failed update does not replace them with current values. Greeting rendering separates the read-only greeting scope from the empty-session initial write scope. Changing character or playthrough cancels old reads and subscriptions. Missing variables and initialization failures surface as errors rather than fabricated default state. The isolated interpreter supplies a finite clean-room display subset: `_.get` (own-property dot/simple bracket paths), `_.isEmpty` (JSON values), `errorCatched(fn)` (reports synchronous and asynchronous exceptions to the rendering error surface), and `$` methods length/ready/text/html/on/val/css/show/hide/addClass/removeClass/empty. These support bounded data display, not full Lodash, jQuery or Helper compatibility. They add no Host, network or write authority. After card scripts register their listeners, startup sends DOMContentLoaded to document and window, followed by load on window; the isolated DOM uses separate event targets. Variable reads still require an available bound snapshot. Scripts depending on SillyTavern's parent-page input or generation button gain no Host DOM access from this lifecycle support. ### Bound greeting selection and events A renderer can pin its observed selected greeting with `scope.greetingIndex`. HTTP accepts `greetingIndex/selectionToken` only in `greeting` and `initial` scopes; historical message scopes cannot attach unverified selection identity. Current greeting snapshots include `viewIdentity:{greetingIndex,selectionToken}`. The token binds the source Host instance, session and selection generation; it is not write authorization. An indexed initial write scope must include this observed token in the existing separate grant scope. Changing A→B, A→B→A, restarting the Host or changing selection before a lazy capability acquisition rejects the old view. The isolated Worker supports a limited first-message read. `getChatMessages(0|'0')` returns only this binding's selected source greeting, after name macros and before display regexes. `SillyTavern.getContext().chat[0]` is the same projection. Its array length is the root session's durable user/assistant message count plus the greeting; other slots are null. Other chat bodies and the full ST context are unavailable. `Mvu.getMvuData({type:'message',message_id:0|'0'})` aliases the corresponding opening snapshot only within this greeting/initial binding. `latest` is accepted only when the greeting is the sole message. Global `getVariables` keeps its strict scope checks, and aliases confer no write permission. Only reading back a different selected greeting index in the same bound session creates a choice notification. After successful Worker startup and listener registration, `tavern_events.CHARACTER_FIRST_MESSAGE_SELECTED` (`character_first_message_selected`) receives `{input,output}` containing the confirmed source text. This is a notification; callbacks cannot rewrite the Host greeting. `tavern_events.MESSAGE_SWIPED` (`message_swiped`) then receives first-message ID 0. Mount, refresh and restart do not fabricate choices or swipes. Failed startup leaves the notification available; a delivered choice is not replayed. Guest DOM-ready calls cannot mint lifecycle events. Delayed writes retain their script/interval cause, observed selection token, separate grant, policy, CAS and source leases. This finite adapter does not promise upstream group-selection middleware, arbitrary message events, prompt injection or parent-page DOM access. ## Current empty-session greeting binding The same snapshot/card-binding/card-write APIs accept the separate scope `{mode:'initial',playthroughId,sessionId,characterId,sessionFormatVersion?}`. It cannot contain nodeId/variantId/endEventId. A session-only management API is not a card capability. Host verifies root-session membership, character selection, empty timeline and one accessible active character resource. Opening configuration updates only the bound session instance; other instances of the same card template remain independent. Initial binding requires a controlled live DSH session. If unloaded, Host restores only the specified session through public `sessionController.resolveAgent(sessionId)`; inability to restore denies access. After the resume lifecycle completes, Host revalidates character selection and membership before acquiring a new binding lease; old capabilities cannot revive. Empty conversation history permits only the three exact configuration event types used by official DSH initialization: `permission/preset`, `sandbox/mode` and `approval/policy`; official `model/selection` with only nonempty string `provider`, `model` and optional nonempty string `reasoningEffort`; explicit user `session/title` with only nonempty string `title`, empty `messageSeqs` and exact `source:{kind:'user'}`; plus the official restore marker `session/end-seed` only when its data is an empty object. Seeded headers (`isSeeded:true`) and markers with inherited/other fields remain rejected. There is no prefix allowlist. Turns, user/assistant messages, inbox activity, unknown events and parent/inherited history are rejected. A synchronous lease captures the session instance/header, event generation, membership and that session's selection generation. `PlayMembershipService.captureLease(playthroughId)` uses `PlayWorkspaceStore.captureReadLease(paths)` to capture process-local mutation tokens for catalog and its timeline plus workspace identity. Every successful public write assigns a fresh token, so restoring the original content hash cannot revive old bindings. Workspace identity changes also invalidate leases even after switching back. This file-level lease requires a fresh binding after any catalog write; unrelated files and failed CAS do not invalidate it. Capabilities do not survive restart, and lease generations are not durable content revisions. Model selection and title changes still advance the event generation and require a fresh binding. Changing a title away and back cannot revive old capabilities. Automatic title sources, message references and unknown title fields are rejected. Starting the first turn or switching the character away and back invalidates old capabilities. Unrelated session selection changes do not invalidate them. Renderer must abort/dispose bindings when leaving or switching sessions/characters, then create a fresh binding on return; old bubbles must never follow current focus. Initial writes retain the independent grant, policy lease, CAS, idempotency and schema checks. Ledger entries use `source.initial:true` for opening configuration rather than inventing assistant messages. The first completed reply inherits the committed current state. Initial mode reads current content and stops reading/writing after the first turn; source management APIs retain the persisted initial records. ## Schema confirmation for explicit built-in adapters A trusted rendering adapter may read available snapshot `variables.mvu_schema:{mvuSchema:1,interpreterVersion:1|2,source}` and compare source exactly with the entire original declaration script selected for substitution. A match means the backend interpreter already handles that declaration. The entire declaration is not executed again in the VM, including initialization and transforms. Missing descriptors, unknown versions and changed sources are rejected. An empty registerMvuSchema function cannot stand in for successful registration. This contract does not provide runtime Zod-object registration or schema hot migration. New declarations compile to interpreter version 2. Stored version 1 descriptors retain their original parsing, root modes and command behavior; reading, restarting or selecting a card does not convert an existing instance. The exact declaration source remains unchanged. For the statically reviewed `https://testingcf.jsdelivr.net/gh/StageDog/tavern_resource/dist/util/mvu_zod.js` registration entry (SHA-256 `78c40f52d81022d9d769a923a49e673b8babb562656051a7d0410b6b19f45184`), version 2 rebuilds only a directly registered root ZodObject as a loose object from its shape, matching that helper. This includes object refinements that retain ZodObject identity; root strictness and root object checks are discarded by that shape reconstruction. Nested schemas keep their own rules. Transform, default/prefault, nullable/optional, union, record and array roots are not mistaken for direct objects. Generic declarations and other import URLs do not receive this automatic root conversion. No upstream import is fetched or executed. The DSL supports `z.looseObject(shape)` and `z.strictObject(shape)` in version 2. Default `z.object` strips unknown keys, loose/passthrough retains them, and strict rejects them. A string-key record permits new dictionary members while validating their values; an enum-key record remains bounded by its enum. Root loose behavior does not recursively allow unknown nested fields. Version 2 Zod command candidates may create missing paths with set or insert. Insert tries an object candidate first and an array candidate if schema validation rejects the object, then keeps only the successfully parsed candidate. Object insertion uses shallow assignment. Set values use the Zod schema for numeric coercion; add still requires an existing number. All creation occurs on private JSON candidates with prototype protection, dense array indices and the existing size/structure limits. Native metadata and version 1 commands retain their earlier path rules. CAS, operation receipts, source schema ownership, grants and usage policies remain required by their existing APIs. This compatibility change does not change the events at which schema validation runs or provide an option to disable validation. Built-in MVU uses local capability contract v1. Exact supported import aliases select `mvu-facade` or `backend-schema` without downloading, hashing or executing the corresponding remote module; upstream bundle repackaging cannot block local capabilities. Only supported invocation forms are accepted. Complete schema declarations still require bounded backend compilation and exact source/interpreter-version confirmation against the authoritative snapshot. Unsupported imports or calls fail explicitly and never fall back to original code. Original-code mode downloads its graph, verifies cached content digests and executes within the existing isolated runtime. Source switches and implementation choices are retained; old original-code caches remain inert and cannot impersonate local capabilities. ## Compatibility and verification Core reference: [MagVarUpdate 183d8ade](https://github.com/MagicalAstrogy/MagVarUpdate/tree/183d8ade3b9a3369e824a55cb13b4ddf91aada50), MIT. Small upstream literal fixtures retain their license. mvu_zod is a semantic reference only; no Helper PolyForm Noncommercial implementation or auxiliary execution code is copied. Acorn interprets a bounded declarative AST, without eval, Function, imports or remote fetching. | Capability | Implementation and boundary | | --- | --- | | Initialization | Bounded YAML/JSON5; reject tags/aliases; merge entries in order | | Commands | set/add/insert/assign/remove/unset/delete; JSONPatch replace/delta/insert/add/remove/move; safe dot/bracket/JSON pointer paths | | Native metadata | extensible/recursiveExtensible/required, object/array templates, arrayMeta/extension marker; remove by value/index; strict/compatible VWD assignment | | Schema | object/array/record/enum/literal/union; number/string/boolean/any/unknown; coerce; default/prefault/optional/nullable; min/max/int; strict/passthrough/strip; transform; custom superRefine; finite regex | | Declaration functions | Synchronous arrow or function expressions; identifier/default parameters; immutable lexical captures; optional member chains; registration wrappers containing a single expression or statement call | | Pure expressions | Arithmetic/comparison/conditions, object spread, constants/locals, input-field assignment, clamp and finite Math functions; static syntax and execution budgets | | Isolation | Private capability brands; persist bounded source + interpreterVersion, rebuild isolated environments; reject array length writes, object numeric/key coercion and prototype paths | | Not equivalent yet | Complete mathjs matrices/units; Date construction/arithmetic; upstream path repair and all tolerant parsing; legacy display_data/delta_data text; Helper write signatures beyond those above and mutable event hooks; MVU auxiliary model generation | Listed schema support is not arbitrary Zod JavaScript support. Method combinations need verification; object-valued coercion is rejected. Current display_data is a result copy and delta_data is an internal change record, not the full legacy UI format. Default instructions request literal JSONPatch; complex generation strategies need their own public contract. `superRefine` retained in the effective schema runs on a frozen JSON copy of validated data and permits only `ctx.addIssue({code:'custom',path?,message?})`. Any issue rejects the candidate without committing state. `Object.prototype.hasOwnProperty.call(data,key)` is an explicit own-property primitive, without exposing Object or prototypes. Declarations may iterate at most 1000 array items with `for (const item of array)`, sharing the function/constraint computation budget. Other loops, this, arguments, async/generators, rest/destructuring parameters and external capabilities remain rejected. This is a restricted [Zod semantic adapter](https://zod.dev/api#superrefine); it does not execute the original declaration script. String `regex` accepts finite patterns anchored with `^...$`, without flags: literals, `\d`, character ranges, grouped alternatives, `?` and `{m,n}` (at most 64 repetitions). Patterns are limited to 256 characters, 16 group levels and 2048 automaton states. Construction, declaration graph freezing and matching consume the shared computation budget, so repeatedly constructing patterns cannot bypass the total limit. Matching uses state sets rather than native RegExp matching. Unbounded repetition, wildcard dots, backreferences and lookarounds are rejected. Interpreter functions, schemas, parameter bindings and AST remain opaque and cannot be read or written as JSON content. The exact full declaration remains in the schema descriptor. Extending compatibility does not reset existing resources, replace IDs or automatically enable managed policies. Tests `node --test test/mvu-state-instances.test.mjs` verifies separate identities, fork/swipe checkpoints, read-only legacy storage, restart, cancellation and commit races. With `DSH_TAVERN_ASSEMBLY_CORE_ROOT` pointing to an official runtime with request assembly, `node --test test/mvu-instance-host.test.mjs` verifies public SessionController creation/forks, real AgentLoop requests and failed-turn regeneration using only temporary data and a synthetic provider. `test/mvu-*.test.mjs` cover neutral structural card fixtures, fixed upstream literals, CAS, forks/history, recovery, adversarial budgets, manager removal and binding cancellation. Set `DSH_TAVERN_PROMPT_COMPAT_ROOT` to a DSH runtime with request assembly support and run `node --test test/mvu-host.test.mjs` for real Host checks with temporary storage and a synthetic provider. Full cards, renderer dependencies and final manager integration require separate verification; interpreter fixtures cannot substitute for them. `test/mvu-history-budget.test.mjs` also covers history collections above the single-state budget, atomic refusal at persistence limits, frozen seeds and cold restore. With that runtime configured, it verifies eight official AgentLoop turns and detached Session recovery. ## Prompt Template dependency reads Trusted Host callers may use `tavernMvu.resolvePromptDependency({id,scope:{authority:'local',sessionId},event:{preview,turn?,step?,usage:'prompt-template-dependency',consumer:{adapterId:'tavern.prompt-templates',id}},signal?})`. The consumer ID is the actual selected template ID; VM code cannot choose the resource, scope or consumer. Types are in `packages/mvu-adapter/src/prompt-dependency.d.ts`. The result is `null` when the source is absent, unavailable or denied, or `{id,adapterId:'tavern.mvu',content,revision,configRevision,checkCurrent}`. Invalid scope, cancellation and configuration errors throw. `content` is a detached whole-variable object, including `stat_data`; world-book sends can read the durable pre-turn checkpoint. This path applies the source's own `before_model_request` policy and fixed read/render/provide chain. Managed resources require explicit permission. For this dependency API, every registered policy handler (including those on native resources) must return an allowed decision with a synchronous lease; an `undefined` abstention denies content release. Native resources with no handlers retain their default permission. Ordinary `resolveRequest` keeps generic handlers' existing abstention behavior, while a registered manager must decide independently; final assembly rechecks its permission and character-binding lease. An ordinary management `read` never grants model retrieval. The Host-only synchronous `checkCurrent()` rejects source revisions, selection changes, catalog/timeline membership ABA, manager reload/removal, cancellation and source unload. Host must be able to attest the session's selection and membership context; unavailable attestation denies access. `PlayMembershipService.captureContextLease()` uses `PlayWorkspaceStore.captureMutationLease()` to attest all public file writes, directory creation and workspace identity changes, including an absent catalog, an unbound workspace and membership ABA. Native sessions do not need a catalog created for them. This conservative lease also expires on unrelated file writes and requires a fresh read. Call only when the template actually reads variables, retain the lease outside the VM, and recheck it at final assembly with no intervening await. Resolving a dependency does not emit an `applied` fact or claim model delivery. This adds no HTTP endpoint or write permission. ## Source precommit command Helpers One complete enabled inline character declaration that registers `global_Mvu_initialized` and `Mvu.events.COMMAND_PARSED` can run in a separate QuickJS VM owned by the source. Each state instance has one effective processor. Repeated reads reuse its initialization promise and receipt; display VM count, visibility and unmounting do not control its lifetime. Card enabled/disabled flags control the source declaration; local rendering switches control display execution only. Conflicting declarations are rejected. Disabling the source declaration or unloading the service invalidates the old registration. Trusted Hosts can also replace it with `registerCommandProcessor`; an old disposer cannot remove the new registration. No HTTP registration endpoint is added. The adapter supplies actual initialization, `eventOn`, `eventMakeLast`, and ordered synchronous `COMMAND_PARSED` callbacks receiving `(variables,commands,messageContent)`. The command view is `{type,args,full_match,reason}` and preserves original order, literal values and escaped JSON Pointer keys. It permits filtering original commands and recovering a single strict JSONPatch missing its value separator, or a single UpdateVariable JSON array. Recovered factory arguments and full_match must match the original operation. New writes, changed values, reordering, unknown syntax and unsafe paths are rejected. Variables are a private baseline copy that callbacks cannot change. Output still passes existing path, schema and atomic apply checks. This subset does not claim every upstream event or dynamic plugin API. The operation gate reads only user text from the same durable turn, at most 64 Ki UTF-16 code units. Oversized text is omitted whole, never truncated into a permitted gate. One operation block is limited to 30,000 code units and must include an execution boundary and operation item. The gate only narrows commands; it does not replace management policy, a card grant or permission. The processor has no DOM, module imports, network, files, Host functions or variable write API. Source is limited to 64 Ki code units, memory to 32 MiB, and each interpreter entry to a 250 ms/interrupt budget; async script callbacks are rejected. After asynchronous Host initialization and scheduling, the original save synchronously rechecks policy checkCurrent, registration generation, source lease, selection/membership, session and captured revision. Both normal and error exits honor revocation without persisting a failure receipt that consumes the cancelled reply. Native execution without a policy handler remains available. Records and binding snapshots may include `commandProcessor:{protocolVersion:1,registered:true,registrationId,source,sha256,listenerCount}`. Rendering compares the complete source and requires this actual receipt before replacing that Helper; the original script is no longer run in every display VM. The receipt proves registration only. Only applied/completed after the atomic save prove a commit. Replaying an already committed source key stops before invoking the processor. Event order and command arguments follow the fixed [MVU update flow](https://github.com/MagicalAstrogy/MagVarUpdate/blob/438f9ffcba95e6c54497fa8d4223f0f48506d35a/src/function/update_variables.ts) and [event types](https://github.com/MagicalAstrogy/MagVarUpdate/blob/438f9ffcba95e6c54497fa8d4223f0f48506d35a/src/variable_def.ts). This bounded independent adapter does not copy an external Helper implementation. ## World-book message-variable macro and source defaults An activated world-book entry containing `{{format_message_variable::stat_data}}` expands at that entry’s original position. After binding and MVU prompt-use policy checks, it renders YAML and recursively removes object keys starting with `$`. Macro characters in values remain literal; multiline values inherit the preceding prefix indentation. Inactive entries and entries without this macro do not read MVU. Denied or unavailable dependencies stop assembly without raw-read fallback. This path adds no standalone MVU preset or duplicate update guidance. The source requests `resolvePromptDependency` with `usage:'world-book-variable'` and the real `tavern.world-books` consumer. Omitting id selects only the current session’s unique active instance. Branches/swipes use their own session state, never the parent’s latest value. Sends with a turn use its durable checkpoint; an older turn cannot read a subsequent reply. Preview reads the explicitly selected current session. Native and new assembly share one activation and policy filter, then synchronously recheck state, binding, registration and policy leases after the final await. Prompt-read leases allow the current turn's claimed input to be logged, official system messages, request context and headers (including strictly `startsSeries:true`), and log-only user/automatic titles. Automatic title references must point to existing or currently claimed user input. These preparation records must not falsely report a world-book policy change. Unknown fields, unclaimed input, future replies, another turn and changes to the existing history still revoke the lease. Initial card writes and command writes retain their full event leases: title changes still revoke old write capabilities. Fork timelines may retain references to the same ancestor session. Current-session prompt reads and source defaults accept those references only when every reference confirms the source’s character card; reads remain bound to the explicit session and state instance. Cross-card references and multiple references without a source character identity remain denied. Any catalog or timeline mutation expires prior leases and requires a fresh read. This grants no historical card writes and never redirects an old session to the current playthrough. `getManagementDefaults({id,scope?})` is synchronous trusted Host API returning null or `{protocolVersion:1,revision,configuration,scopePolicy:'source-bound',checkCurrent}`. Configuration contains only type/store/retrieve. MVU defaults support assistant commit parse/validate/apply and model read/render/provide. Eligibility covers source-validated bindings; it grants no wildcard, card write or automatic prompt injection. Registration, removal, source and binding changes expire leases. The manager composes source defaults → local overrides → existing preset precedence. Explicit empty lists, empty store/retrieve and deny rules remain denied. A registered manager with invalid configuration stays managed and denies; removal restores source defaults while explicit generic source denials remain effective.