@storylet-studio/runtime 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { ScalarValue, ScopeResolver, ExprNode } from '@wildwinter/expr';
1
+ import { ScalarValue, ScopeResolver, ExprNode, EvalContext } from '@wildwinter/expr';
2
2
  export { Prng, makePrng, shuffleInPlace } from '@wildwinter/expr';
3
- import { Bundle, FlowSave, LoadReport, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, SaveEnvelope, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
- import { PropertyBag, PropertyRow } from '@wildwinter/scoperegistry';
3
+ import { Bundle, FlowSave, LoadReport, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, SaveEnvelope, SaveEnvelopeV1, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
+ import { PropertyBag, ScopeRegistry, PropertyRow } from '@wildwinter/scoperegistry';
5
5
  export { PropertyRow } from '@wildwinter/scoperegistry';
6
6
 
7
7
  interface EngineOptions {
@@ -18,12 +18,25 @@ interface EngineOptions {
18
18
  };
19
19
  /**
20
20
  * The host's resolver for @world - the values the game owns and the
21
- * story reads (and, where `set` is offered, writes). Omit it and the
22
- * engine self-backs @world from the declared defaults. Engine-level,
23
- * shared by all flows, never in saveGame(): the host saves its container
24
- * once, each engine saves its own envelope (design/flows.md).
21
+ * story reads (and, where `set` is offered, writes). Engine-level, shared
22
+ * by all flows, never saved: the game keeps these values. Omit it and a
23
+ * standalone engine self-backs @world from the declared defaults, as a
24
+ * property its registry stores and saves. A game running several engines
25
+ * registers @world in its registry itself instead.
25
26
  */
26
27
  world?: ScopeResolver;
28
+ /**
29
+ * The game's registry: ONE per game, holding every engine's properties
30
+ * except those the game keeps itself, saved once. Given one, the engine
31
+ * registers its own scopes in it (@story under `story`, every other bag
32
+ * under a key starting `storylets/`, and @world if `world` is passed),
33
+ * reads every other scope from it, and `saveGame()` leaves the property
34
+ * values to the game. @world is then the game's to register: owned if the
35
+ * registry should store it, foreign if the game keeps it. Omit it and the
36
+ * engine makes its own registry and acts as its own game: it self-backs
37
+ * @world, and `saveGame()` carries the registry's values too.
38
+ */
39
+ registry?: ScopeRegistry;
27
40
  /**
28
41
  * Diagnostics hook (opt-in, dev tooling only): fired when `openFlow` REPLACES
29
42
  * a flow that still had cards dealt, with the flow id and how many. The
@@ -313,9 +326,24 @@ interface Internals {
313
326
  * what the shared side WOULD hold without building a bag, which is what
314
327
  * makes previewLoad pure. */
315
328
  sharedDecls: DeclSet;
316
- /** The shared stores. Reassigned wholesale by loadGame/reset. */
329
+ /** The shared stores, registered in the registry for the engine's life.
330
+ * Reseeded in place by reset and by a load that carries values. */
317
331
  shared: Partition;
318
- /** @world: the host's resolver, or the self-backed bag's. */
332
+ /** The game's one registry (or the engine's own, when it is standalone). */
333
+ registry: ScopeRegistry;
334
+ /** True when the engine made the registry: `saveGame()` then carries its values. */
335
+ ownsRegistry: boolean;
336
+ /** True when the engine self-backed @world (standalone, no resolver bound). */
337
+ selfWorld: boolean;
338
+ /** Every OTHER scope in the registry, as an eval context sees it (instance
339
+ * keys left out), rebuilt only when the registry's set of scopes moves. */
340
+ registryView: () => {
341
+ scopes: EvalContext["scopes"];
342
+ qualities: EvalContext["qualities"];
343
+ };
344
+ /** @world, read through the registry by name (the scope's own normalisation),
345
+ * so a @world the game registered folded to lower case still answers the
346
+ * names as authored. */
319
347
  worldResolver: ScopeResolver;
320
348
  /** The @world WRITE seam. `host` says the caller is the GAME's own surface -
321
349
  * setProperty, the coverage harness, the CLI's --set - which the shared
@@ -348,10 +376,19 @@ declare class Engine {
348
376
  * outlives reset/loadGame (the host's container is the host's). The
349
377
  * self-backed resolver is rebuilt instead. */
350
378
  private readonly hostWorld?;
379
+ /** The options this engine was built with: hotSwap builds its replacement from them. */
380
+ private readonly creationOptions;
381
+ /** How to register each shared scope again, in registration order: a failed
382
+ * hotSwap puts this engine back exactly as it was. */
383
+ private readonly sharedMounts;
351
384
  constructor(bundle: Bundle, opts?: EngineOptions);
352
- /** Build the shared stores and the @world seam. `hostWorld` sticks for the
353
- * engine's lifetime; reset/loadGame rebuild the shared bags around it. */
385
+ /** Build the shared stores, register them and @world, and set up the @world
386
+ * seam. Once, for the engine's life: reset and loads reseed the bags in
387
+ * place, so the registry never sees them come and go. */
354
388
  private initShared;
389
+ /** Every shared bag back to its declared defaults, in place (the registry
390
+ * keeps them registered), the self-backed @world included. */
391
+ private reseedShared;
355
392
  /** Quality ladders by scope for the eval channel (design/quality.md):
356
393
  * world/story keyed by name; box/deck/value keyed by owner id then name.
357
394
  * Built once - a bundle's declarations never change. Ladders are
@@ -362,6 +399,10 @@ declare class Engine {
362
399
  * state; shared state is untouched. There is no default flow: "main" is
363
400
  * a caller convention, not an engine rule. */
364
401
  openFlow(id: string, opts?: OpenFlowOptions): Flow;
402
+ /** openFlow, and loadGame's rebuild. `claim` says the new flow's bags take
403
+ * the values the registry holds for them (a load); a fresh open is a reset
404
+ * of that name, so anything waiting for it is discarded first. */
405
+ private open;
365
406
  getFlow(id: string): Flow | undefined;
366
407
  /** Every live flow, open order. */
367
408
  flows(): Flow[];
@@ -376,6 +417,11 @@ declare class Engine {
376
417
  * self-backed @world included; a host-bound @world is the host's and is
377
418
  * not touched). */
378
419
  reset(): void;
420
+ /** End the run: clear the log, close every flow, forget spent cards. Each
421
+ * flow's bags leave the registry; `keepFlows` names the flows whose values
422
+ * are kept there for the flow that replaces them (a load into the game's
423
+ * registry). */
424
+ private dropRun;
379
425
  /** Cards a shared `redraw: "never"` has taken out of the world, by card id.
380
426
  * The claim ledger is DERIVED from live boards and so needs no storage;
381
427
  * this one is durable, so it rides the save's shared half. */
@@ -418,6 +464,12 @@ declare class Engine {
418
464
  * carries no flow. It reaches the run log and the engine tap; there is
419
465
  * nowhere else for it to go, and it fires only on a legacy address. */
420
466
  private diagnose;
467
+ /** Content that names another engine's scope (`@patter.visits`) runs only
468
+ * where that engine is on this registry: without it every read would answer
469
+ * false and every write fail, so the flow is refused as it opens, before
470
+ * anything changes. By then a game has built all of its engines, whatever
471
+ * order it built them in. The same message on every runtime. */
472
+ private assertExternalScopes;
421
473
  /** The shared surface as examiner rows: @world (read through the
422
474
  * resolver) then the shared partitions. Per-flow rows live on each Flow. */
423
475
  listProperties(): PropertyRow[];
@@ -427,9 +479,33 @@ declare class Engine {
427
479
  listBags(): BagMount[];
428
480
  /** Every flow's trace, one stream, each event tagged with its flow id. */
429
481
  subscribeTrace(handler: EngineTraceHandler): () => void;
430
- /** The whole engine, one envelope: the shared partitions once, then
431
- * every live flow keyed by its id. @world is NEVER here - the host
432
- * saves its container, each engine saves its own envelope. */
482
+ /**
483
+ * Live bundle refresh: rebuild on an edited bundle with the whole run carried
484
+ * over, and return the replacement with the report its load produced.
485
+ *
486
+ * Standalone, that is a save and a load into a new engine, and this one is left
487
+ * untouched (discard it). With the game's registry the two cannot both hold the
488
+ * same keys, so this engine is spent afterwards (its flows closed, the
489
+ * replacement holding everything on the same registry): it carries its
490
+ * own values into the snapshot, steps out of the registry, and the replacement
491
+ * loads them the way a standalone save loads: so the report covers the
492
+ * properties the edit dropped, defaulted, or retyped, and a dropped property
493
+ * is dropped rather than kept. Values the game loaded that were still waiting
494
+ * for a flow of this engine carry across as they were, and nothing belonging
495
+ * to any other engine is touched. A save for another project is refused before
496
+ * anything moves; if the rebuild fails for any other reason, this engine takes
497
+ * its registrations back and is left exactly as it was.
498
+ */
499
+ hotSwap(bundle: Bundle, opts?: EngineOptions): {
500
+ engine: Engine;
501
+ report: LoadReport;
502
+ };
503
+ /** The whole engine's NON-property state, one envelope: the spent cards
504
+ * once, then every live flow (board, clocks, cooldowns, PRNG, play log)
505
+ * keyed by its id. The property values are the registry's: a standalone
506
+ * engine (one that made its own registry) carries them here under
507
+ * `registry`, self-backed @world included; a game that passed a registry
508
+ * saves it once itself, beside each engine's envelope. */
433
509
  saveGame(): SaveEnvelope;
434
510
  /** ONE flow's blob, to park a visit that is walking away: the same shape
435
511
  * the envelope carries per flow, and the same shape `openFlow`'s `restore`
@@ -442,7 +518,7 @@ declare class Engine {
442
518
  * doing any of it (design/engine-server.md 4.9). Pure: nothing on this
443
519
  * engine moves. A project mismatch is refused here exactly as `loadGame`
444
520
  * refuses it - it is the one thing neither call will tolerate. */
445
- previewLoad(envelope: SaveEnvelope): LoadReport;
521
+ previewLoad(envelope: SaveEnvelope | SaveEnvelopeV1): LoadReport;
446
522
  /** What `openFlow(id, { restore: saved })` would do to a flow of that name,
447
523
  * without doing it: the same report shape, since a visit parked under one
448
524
  * build and resumed under the next raises the same questions. Pure. */
@@ -451,10 +527,18 @@ declare class Engine {
451
527
  * Handles held from before the load are closed and inert (Patter's
452
528
  * rule); take fresh ones from getFlow()/flows().
453
529
  *
530
+ * Property values come from the registry. An envelope that carries them
531
+ * (a standalone engine's, or a version 1 envelope) has them walked, cleaned,
532
+ * and moved into the registry here, over fresh defaults. Otherwise the game
533
+ * loads its registry itself, before or after this call: each flow's bags
534
+ * are handed back to the registry with their values, and the restored
535
+ * flows claim them. The report then covers only what this envelope holds;
536
+ * the registry's own load rule applies to the values.
537
+ *
454
538
  * Returns the report `previewLoad` would have given for this envelope: the
455
539
  * drift tolerance that makes a load forgiving is what hides its cost, so
456
540
  * the cost comes back with the load whether or not anybody looked first. */
457
- loadGame(envelope: SaveEnvelope): LoadReport;
541
+ loadGame(envelope: SaveEnvelope | SaveEnvelopeV1): LoadReport;
458
542
  private assertSameProject;
459
543
  /** The whole-envelope walk: the report, and the cleaned state the apply
460
544
  * half writes. Nothing here touches the engine, which is what lets
@@ -482,8 +566,12 @@ declare class Flow {
482
566
  private lastPlayOf;
483
567
  private tagPlayCount;
484
568
  private lastPlayInTag;
485
- /** The per-flow property partitions (the not-shared halves). */
569
+ /** The per-flow property partitions (the not-shared halves), each bag
570
+ * that declares something registered under this flow's keys. */
486
571
  private stores;
572
+ private readonly registered;
573
+ /** This flow's bags that declare something, under their registry keys. */
574
+ private readonly bagKeys;
487
575
  private traceHandlers;
488
576
  private logEntries;
489
577
  private logSeq;
@@ -501,6 +589,15 @@ declare class Flow {
501
589
  close(): void;
502
590
  /** @internal */
503
591
  markClosed(): void;
592
+ /** @internal - take this flow's bags out of the registry; with `keep`, their
593
+ * values wait there for the flow that replaces this one (a load into the
594
+ * game's registry). Idempotent. */
595
+ releaseBags(keep: boolean): void;
596
+ /** @internal - the registry keys this flow holds right now. */
597
+ registeredKeys(): string[];
598
+ /** @internal - register this flow's bags (again): at construction, and when a
599
+ * failed hotSwap hands them back. Each claims what the registry holds for it. */
600
+ mountBags(): void;
504
601
  private assertOpen;
505
602
  /** A box's current turn (schema 3.4), on THIS flow's clock. */
506
603
  turn(boxRef: string): number;
@@ -709,8 +806,9 @@ declare class Flow {
709
806
  getProperty(path: string): ScalarValue;
710
807
  setProperty(path: string, value: ScalarValue): void;
711
808
  private resolvePath;
712
- /** @internal - this flow's blob inside the engine's envelope. */
713
- snapshot(): FlowSave;
809
+ /** @internal - this flow's blob: inside the engine's envelope without its
810
+ * properties (the registry has them), or parked whole by saveFlow. */
811
+ snapshot(withProps: boolean): FlowSave;
714
812
  /** @internal - restore a freshly opened flow from its blob (loadGame).
715
813
  * Orphaned keys (deleted entities) drop; new declarations keep defaults. */
716
814
  restore(saved: FlowSave): void;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { ScalarValue, ScopeResolver, ExprNode } from '@wildwinter/expr';
1
+ import { ScalarValue, ScopeResolver, ExprNode, EvalContext } from '@wildwinter/expr';
2
2
  export { Prng, makePrng, shuffleInPlace } from '@wildwinter/expr';
3
- import { Bundle, FlowSave, LoadReport, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, SaveEnvelope, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
- import { PropertyBag, PropertyRow } from '@wildwinter/scoperegistry';
3
+ import { Bundle, FlowSave, LoadReport, Card, Expression, Deck, Box, Hand, HandTemplate, TagGroup, PropertyDecl, SaveEnvelope, SaveEnvelopeV1, PropertyType, ScalarValue as ScalarValue$1 } from '@storylet-studio/model';
4
+ import { PropertyBag, ScopeRegistry, PropertyRow } from '@wildwinter/scoperegistry';
5
5
  export { PropertyRow } from '@wildwinter/scoperegistry';
6
6
 
7
7
  interface EngineOptions {
@@ -18,12 +18,25 @@ interface EngineOptions {
18
18
  };
19
19
  /**
20
20
  * The host's resolver for @world - the values the game owns and the
21
- * story reads (and, where `set` is offered, writes). Omit it and the
22
- * engine self-backs @world from the declared defaults. Engine-level,
23
- * shared by all flows, never in saveGame(): the host saves its container
24
- * once, each engine saves its own envelope (design/flows.md).
21
+ * story reads (and, where `set` is offered, writes). Engine-level, shared
22
+ * by all flows, never saved: the game keeps these values. Omit it and a
23
+ * standalone engine self-backs @world from the declared defaults, as a
24
+ * property its registry stores and saves. A game running several engines
25
+ * registers @world in its registry itself instead.
25
26
  */
26
27
  world?: ScopeResolver;
28
+ /**
29
+ * The game's registry: ONE per game, holding every engine's properties
30
+ * except those the game keeps itself, saved once. Given one, the engine
31
+ * registers its own scopes in it (@story under `story`, every other bag
32
+ * under a key starting `storylets/`, and @world if `world` is passed),
33
+ * reads every other scope from it, and `saveGame()` leaves the property
34
+ * values to the game. @world is then the game's to register: owned if the
35
+ * registry should store it, foreign if the game keeps it. Omit it and the
36
+ * engine makes its own registry and acts as its own game: it self-backs
37
+ * @world, and `saveGame()` carries the registry's values too.
38
+ */
39
+ registry?: ScopeRegistry;
27
40
  /**
28
41
  * Diagnostics hook (opt-in, dev tooling only): fired when `openFlow` REPLACES
29
42
  * a flow that still had cards dealt, with the flow id and how many. The
@@ -313,9 +326,24 @@ interface Internals {
313
326
  * what the shared side WOULD hold without building a bag, which is what
314
327
  * makes previewLoad pure. */
315
328
  sharedDecls: DeclSet;
316
- /** The shared stores. Reassigned wholesale by loadGame/reset. */
329
+ /** The shared stores, registered in the registry for the engine's life.
330
+ * Reseeded in place by reset and by a load that carries values. */
317
331
  shared: Partition;
318
- /** @world: the host's resolver, or the self-backed bag's. */
332
+ /** The game's one registry (or the engine's own, when it is standalone). */
333
+ registry: ScopeRegistry;
334
+ /** True when the engine made the registry: `saveGame()` then carries its values. */
335
+ ownsRegistry: boolean;
336
+ /** True when the engine self-backed @world (standalone, no resolver bound). */
337
+ selfWorld: boolean;
338
+ /** Every OTHER scope in the registry, as an eval context sees it (instance
339
+ * keys left out), rebuilt only when the registry's set of scopes moves. */
340
+ registryView: () => {
341
+ scopes: EvalContext["scopes"];
342
+ qualities: EvalContext["qualities"];
343
+ };
344
+ /** @world, read through the registry by name (the scope's own normalisation),
345
+ * so a @world the game registered folded to lower case still answers the
346
+ * names as authored. */
319
347
  worldResolver: ScopeResolver;
320
348
  /** The @world WRITE seam. `host` says the caller is the GAME's own surface -
321
349
  * setProperty, the coverage harness, the CLI's --set - which the shared
@@ -348,10 +376,19 @@ declare class Engine {
348
376
  * outlives reset/loadGame (the host's container is the host's). The
349
377
  * self-backed resolver is rebuilt instead. */
350
378
  private readonly hostWorld?;
379
+ /** The options this engine was built with: hotSwap builds its replacement from them. */
380
+ private readonly creationOptions;
381
+ /** How to register each shared scope again, in registration order: a failed
382
+ * hotSwap puts this engine back exactly as it was. */
383
+ private readonly sharedMounts;
351
384
  constructor(bundle: Bundle, opts?: EngineOptions);
352
- /** Build the shared stores and the @world seam. `hostWorld` sticks for the
353
- * engine's lifetime; reset/loadGame rebuild the shared bags around it. */
385
+ /** Build the shared stores, register them and @world, and set up the @world
386
+ * seam. Once, for the engine's life: reset and loads reseed the bags in
387
+ * place, so the registry never sees them come and go. */
354
388
  private initShared;
389
+ /** Every shared bag back to its declared defaults, in place (the registry
390
+ * keeps them registered), the self-backed @world included. */
391
+ private reseedShared;
355
392
  /** Quality ladders by scope for the eval channel (design/quality.md):
356
393
  * world/story keyed by name; box/deck/value keyed by owner id then name.
357
394
  * Built once - a bundle's declarations never change. Ladders are
@@ -362,6 +399,10 @@ declare class Engine {
362
399
  * state; shared state is untouched. There is no default flow: "main" is
363
400
  * a caller convention, not an engine rule. */
364
401
  openFlow(id: string, opts?: OpenFlowOptions): Flow;
402
+ /** openFlow, and loadGame's rebuild. `claim` says the new flow's bags take
403
+ * the values the registry holds for them (a load); a fresh open is a reset
404
+ * of that name, so anything waiting for it is discarded first. */
405
+ private open;
365
406
  getFlow(id: string): Flow | undefined;
366
407
  /** Every live flow, open order. */
367
408
  flows(): Flow[];
@@ -376,6 +417,11 @@ declare class Engine {
376
417
  * self-backed @world included; a host-bound @world is the host's and is
377
418
  * not touched). */
378
419
  reset(): void;
420
+ /** End the run: clear the log, close every flow, forget spent cards. Each
421
+ * flow's bags leave the registry; `keepFlows` names the flows whose values
422
+ * are kept there for the flow that replaces them (a load into the game's
423
+ * registry). */
424
+ private dropRun;
379
425
  /** Cards a shared `redraw: "never"` has taken out of the world, by card id.
380
426
  * The claim ledger is DERIVED from live boards and so needs no storage;
381
427
  * this one is durable, so it rides the save's shared half. */
@@ -418,6 +464,12 @@ declare class Engine {
418
464
  * carries no flow. It reaches the run log and the engine tap; there is
419
465
  * nowhere else for it to go, and it fires only on a legacy address. */
420
466
  private diagnose;
467
+ /** Content that names another engine's scope (`@patter.visits`) runs only
468
+ * where that engine is on this registry: without it every read would answer
469
+ * false and every write fail, so the flow is refused as it opens, before
470
+ * anything changes. By then a game has built all of its engines, whatever
471
+ * order it built them in. The same message on every runtime. */
472
+ private assertExternalScopes;
421
473
  /** The shared surface as examiner rows: @world (read through the
422
474
  * resolver) then the shared partitions. Per-flow rows live on each Flow. */
423
475
  listProperties(): PropertyRow[];
@@ -427,9 +479,33 @@ declare class Engine {
427
479
  listBags(): BagMount[];
428
480
  /** Every flow's trace, one stream, each event tagged with its flow id. */
429
481
  subscribeTrace(handler: EngineTraceHandler): () => void;
430
- /** The whole engine, one envelope: the shared partitions once, then
431
- * every live flow keyed by its id. @world is NEVER here - the host
432
- * saves its container, each engine saves its own envelope. */
482
+ /**
483
+ * Live bundle refresh: rebuild on an edited bundle with the whole run carried
484
+ * over, and return the replacement with the report its load produced.
485
+ *
486
+ * Standalone, that is a save and a load into a new engine, and this one is left
487
+ * untouched (discard it). With the game's registry the two cannot both hold the
488
+ * same keys, so this engine is spent afterwards (its flows closed, the
489
+ * replacement holding everything on the same registry): it carries its
490
+ * own values into the snapshot, steps out of the registry, and the replacement
491
+ * loads them the way a standalone save loads: so the report covers the
492
+ * properties the edit dropped, defaulted, or retyped, and a dropped property
493
+ * is dropped rather than kept. Values the game loaded that were still waiting
494
+ * for a flow of this engine carry across as they were, and nothing belonging
495
+ * to any other engine is touched. A save for another project is refused before
496
+ * anything moves; if the rebuild fails for any other reason, this engine takes
497
+ * its registrations back and is left exactly as it was.
498
+ */
499
+ hotSwap(bundle: Bundle, opts?: EngineOptions): {
500
+ engine: Engine;
501
+ report: LoadReport;
502
+ };
503
+ /** The whole engine's NON-property state, one envelope: the spent cards
504
+ * once, then every live flow (board, clocks, cooldowns, PRNG, play log)
505
+ * keyed by its id. The property values are the registry's: a standalone
506
+ * engine (one that made its own registry) carries them here under
507
+ * `registry`, self-backed @world included; a game that passed a registry
508
+ * saves it once itself, beside each engine's envelope. */
433
509
  saveGame(): SaveEnvelope;
434
510
  /** ONE flow's blob, to park a visit that is walking away: the same shape
435
511
  * the envelope carries per flow, and the same shape `openFlow`'s `restore`
@@ -442,7 +518,7 @@ declare class Engine {
442
518
  * doing any of it (design/engine-server.md 4.9). Pure: nothing on this
443
519
  * engine moves. A project mismatch is refused here exactly as `loadGame`
444
520
  * refuses it - it is the one thing neither call will tolerate. */
445
- previewLoad(envelope: SaveEnvelope): LoadReport;
521
+ previewLoad(envelope: SaveEnvelope | SaveEnvelopeV1): LoadReport;
446
522
  /** What `openFlow(id, { restore: saved })` would do to a flow of that name,
447
523
  * without doing it: the same report shape, since a visit parked under one
448
524
  * build and resumed under the next raises the same questions. Pure. */
@@ -451,10 +527,18 @@ declare class Engine {
451
527
  * Handles held from before the load are closed and inert (Patter's
452
528
  * rule); take fresh ones from getFlow()/flows().
453
529
  *
530
+ * Property values come from the registry. An envelope that carries them
531
+ * (a standalone engine's, or a version 1 envelope) has them walked, cleaned,
532
+ * and moved into the registry here, over fresh defaults. Otherwise the game
533
+ * loads its registry itself, before or after this call: each flow's bags
534
+ * are handed back to the registry with their values, and the restored
535
+ * flows claim them. The report then covers only what this envelope holds;
536
+ * the registry's own load rule applies to the values.
537
+ *
454
538
  * Returns the report `previewLoad` would have given for this envelope: the
455
539
  * drift tolerance that makes a load forgiving is what hides its cost, so
456
540
  * the cost comes back with the load whether or not anybody looked first. */
457
- loadGame(envelope: SaveEnvelope): LoadReport;
541
+ loadGame(envelope: SaveEnvelope | SaveEnvelopeV1): LoadReport;
458
542
  private assertSameProject;
459
543
  /** The whole-envelope walk: the report, and the cleaned state the apply
460
544
  * half writes. Nothing here touches the engine, which is what lets
@@ -482,8 +566,12 @@ declare class Flow {
482
566
  private lastPlayOf;
483
567
  private tagPlayCount;
484
568
  private lastPlayInTag;
485
- /** The per-flow property partitions (the not-shared halves). */
569
+ /** The per-flow property partitions (the not-shared halves), each bag
570
+ * that declares something registered under this flow's keys. */
486
571
  private stores;
572
+ private readonly registered;
573
+ /** This flow's bags that declare something, under their registry keys. */
574
+ private readonly bagKeys;
487
575
  private traceHandlers;
488
576
  private logEntries;
489
577
  private logSeq;
@@ -501,6 +589,15 @@ declare class Flow {
501
589
  close(): void;
502
590
  /** @internal */
503
591
  markClosed(): void;
592
+ /** @internal - take this flow's bags out of the registry; with `keep`, their
593
+ * values wait there for the flow that replaces this one (a load into the
594
+ * game's registry). Idempotent. */
595
+ releaseBags(keep: boolean): void;
596
+ /** @internal - the registry keys this flow holds right now. */
597
+ registeredKeys(): string[];
598
+ /** @internal - register this flow's bags (again): at construction, and when a
599
+ * failed hotSwap hands them back. Each claims what the registry holds for it. */
600
+ mountBags(): void;
504
601
  private assertOpen;
505
602
  /** A box's current turn (schema 3.4), on THIS flow's clock. */
506
603
  turn(boxRef: string): number;
@@ -709,8 +806,9 @@ declare class Flow {
709
806
  getProperty(path: string): ScalarValue;
710
807
  setProperty(path: string, value: ScalarValue): void;
711
808
  private resolvePath;
712
- /** @internal - this flow's blob inside the engine's envelope. */
713
- snapshot(): FlowSave;
809
+ /** @internal - this flow's blob: inside the engine's envelope without its
810
+ * properties (the registry has them), or parked whole by saveFlow. */
811
+ snapshot(withProps: boolean): FlowSave;
714
812
  /** @internal - restore a freshly opened flow from its blob (loadGame).
715
813
  * Orphaned keys (deleted entities) drop; new declarations keep defaults. */
716
814
  restore(saved: FlowSave): void;