@patterkit/ops 0.13.0 → 0.14.0

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +207 -29
  2. package/dist/index.js +948 -476
  3. package/package.json +5 -5
package/dist/index.d.ts CHANGED
@@ -244,11 +244,21 @@ interface ValidateResult {
244
244
  /** The game's shared scopes folder: its files, and how the project stands against them. Only the
245
245
  * errors count against `ok`. Empty when the project has no folder. */
246
246
  gameScopes: GameScopesIssue[];
247
+ /** The loc shards against each other and the project: a key two shards give different text (an
248
+ * error: the compile refuses it), and as warnings a shard for a scene the project lacks, one in a
249
+ * language it does not declare, or two shards for one scene and language. */
250
+ localisation: GameScopesIssue[];
247
251
  /** Only ERRORS count: a warning (another tool's scope, say) never fails a build. */
248
252
  ok: boolean;
249
253
  }
250
254
  /** Run structural + expression + interpolation + hygiene + bundle-staleness + merge validation. */
251
255
  declare function runValidate(loaded: LoadedProject): ValidateResult;
256
+ /**
257
+ * The content errors a bundle may not be built over, one line each: structural errors (not warnings),
258
+ * condition and interpolation errors, and loc shards that disagree. Not hygiene, staleness, orphans or
259
+ * reachability: those say nothing about whether the bundle works. `runExport` refuses on these.
260
+ */
261
+ declare function exportBlockers(loaded: LoadedProject): string[];
252
262
 
253
263
  /**
254
264
  * Conditions that can never hold, as warnings. Empty for a project with no monotonic latches, which is
@@ -256,17 +266,122 @@ declare function runValidate(loaded: LoadedProject): ValidateResult;
256
266
  */
257
267
  declare function reachabilityIssues(loaded: LoadedProject, compiled?: Bundle): ConditionIssue[];
258
268
 
259
- declare function runExport(loaded: LoadedProject): Bundle;
269
+ /** A recording ladder rung as the indexer needs it: a name and its (optional) project-relative folder. */
270
+ interface AudioRung {
271
+ name: string;
272
+ folder?: string;
273
+ }
274
+ /** One resolved beat: the rung it landed in, the absolute path to its audio file, and (for scratch takes)
275
+ * the text-hash stamped in the WAV so the editor can flag a take that's gone stale against its line. */
276
+ interface AudioEntry {
277
+ status: string;
278
+ path: string;
279
+ textHash?: string;
280
+ }
281
+ /** beatId -> resolved audio. Absent ids are implicitly "missing". */
282
+ type AudioSnapshot = Record<string, AudioEntry>;
283
+ /** The sidecar audio manifest (`patteraudio.json`): each beat's winning clip, keyed by beatId, with its
284
+ * path relative to the audio root (forward-slashed) + the rung it resolved to. Consumed by the runtimes'
285
+ * audio resolvers. Kept OUT of the .patterc so audio stays decoupled from story rebuilds. */
286
+ declare const AUDIO_MANIFEST_SCHEMA = "patter/audio@0";
287
+ declare const AUDIO_MANIFEST_FILE = "patteraudio.json";
288
+ /** Whether a project derives recording status from folders at all: voiced, tracking audio status, Audio
289
+ * Folders on, and an audio root set. */
290
+ declare function audioFoldersActive(p: ProjectFile): boolean;
291
+ /** The ladder's rungs that have a folder, HIGHEST priority first (the most-finished take wins), with each
292
+ * folder made absolute against the project root. */
293
+ declare function audioFolderRungs(root: string, rungs: AudioRung[]): Array<{
294
+ name: string;
295
+ dir: string;
296
+ }>;
297
+ /** The snapshot from each rung folder's listing, highest rung first (`undefined` files = a folder that is
298
+ * missing or unreadable, treated as empty). Within a folder a `.wav` beats an `.mp3` of the same id; a
299
+ * higher rung that already claimed an id wins overall. */
300
+ declare function pickAudio(listings: Array<{
301
+ name: string;
302
+ dir: string;
303
+ files: string[] | undefined;
304
+ }>): AudioSnapshot;
305
+ /** One synchronous scan of a project's audio folders, or undefined when it does not use them. */
306
+ declare function scanAudio(loaded: LoadedProject): AudioSnapshot | undefined;
307
+ /** beatId -> derived recording status, for the report and the voice script; undefined when the project
308
+ * doesn't derive status from folders, so callers fall back to the manual recording map as Patterpad does. */
309
+ declare function scanAudioStatus(loaded: LoadedProject): Map<string, string> | undefined;
310
+ /** Serialise a snapshot into the sidecar manifest JSON, with each absolute path made root-relative
311
+ * (forward-slashed) against `<projectRoot>/<audioRoot>`. */
312
+ declare function audioManifest(snapshot: AudioSnapshot, projectRoot: string, audioRoot: string): string;
313
+ /** The manifest write for a snapshot, beside the audio; undefined when the project does not use Audio
314
+ * Folders or no take has been found yet. */
315
+ declare function audioManifestWrite(loaded: LoadedProject, snapshot: AudioSnapshot | undefined): PlannedWrite | undefined;
316
+
317
+ /** What a shipped build may not be built over. */
318
+ interface ExportOptions {
319
+ /** Build even when validate finds errors in the content (a dangling jump, a condition that does not
320
+ * parse): the CLI's `--allow-invalid`. Never what a game wants; for looking at a broken build. */
321
+ allowInvalid?: boolean;
322
+ }
323
+ /** Compile a loaded project to the runtime bundle, applying its build localisation mode (spec §11):
324
+ * "embedded" (default) keeps every locale's strings inline; "ids" strips them so the runtime emits beat
325
+ * IDs (the game localises). `sourceDebug` keeps only the SOURCE locale, embedded for debug playback and
326
+ * flagged so the runtime warns it is not a shippable build. Refuses an unresolved merge, and content
327
+ * validate calls broken unless `allowInvalid`. */
328
+ declare function runExport(loaded: LoadedProject, opts?: ExportOptions): Bundle;
260
329
  /** Compile WITHOUT the localisation-mode transform - the full bundle with every locale inline. Used where
261
- * the host needs all strings regardless of build mode (e.g. Patterpad's Play window + Export Localisation). */
262
- declare function runExportFull(loaded: LoadedProject): Bundle;
330
+ * the host needs all strings regardless of build mode (e.g. Patterpad's Play window + Export Localisation).
331
+ * Refuses an unresolved merge; broken content only with `refuseInvalid: true` (the playable page), since
332
+ * the Play window exists to try content mid-edit. */
333
+ declare function runExportFull(loaded: LoadedProject, opts?: ExportOptions & {
334
+ refuseInvalid?: boolean;
335
+ }): Bundle;
336
+ /** Everything a build writes, for a front end to commit. */
337
+ interface BuildPlan {
338
+ bundle: Bundle;
339
+ /** The bundle as written: strict JSON, since every runtime reads it with a stock parser. */
340
+ text: string;
341
+ /** The bundle, `patter.scopes.json` when the game's scopes folder needs it brought up to date, and the
342
+ * audio manifest when the project uses Audio Folders and has takes. The bundle first. */
343
+ writes: PlannedWrite[];
344
+ }
345
+ /**
346
+ * Plan a build: the bundle and everything that goes with it, so every front end writes the same set
347
+ * (CLI review 2026-10, item 23). Patterpad's Build wrote bundle, scopes and audio manifest; its Auto
348
+ * Rebuild skipped the scopes file; `patter export` never wrote the manifest. `bundlePath` overrides
349
+ * where the bundle goes (the CLI's `-o`); `audio` is a snapshot the caller already holds (Patterpad's
350
+ * live index), else the folders are scanned now.
351
+ */
352
+ declare function planBuild(loaded: LoadedProject, opts?: ExportOptions & {
353
+ bundlePath?: string;
354
+ audio?: AudioSnapshot;
355
+ }): BuildPlan;
356
+
357
+ /**
358
+ * Compile a loaded project exactly as `export` does, with nothing refused: the one compile every analysis
359
+ * uses (validate's staleness check, reachability, play, coverage). Five sites each compiled the project
360
+ * themselves and had drifted from export: none stripped the contentless beats export strips, so one blank
361
+ * line made a freshly exported bundle read as stale for ever, and coverage counted beats that never ship.
362
+ */
363
+ declare function compileLoaded(loaded: LoadedProject): Bundle;
263
364
  /**
264
- * The conventional output path for the compiled `.patterc` bundle (spec §11):
265
- * the project's `export.bundle` (relative to the root, or absolute) if set,
266
- * else `dist/<project-file-stem>.patterc`. This is where `patter export` writes
267
- * with no `-o`, and a stable place the staleness gate (validate) can find.
365
+ * The default bundle location, relative to the project root, used when the project pins no
366
+ * `export.bundle`: a SIBLING `patter-dist/` folder, not inside the project. On macOS the project folder is
367
+ * a `.patter` package, and build output buried in the document is output nobody finds. One default for
368
+ * every front end: the CLI wrote `dist/` inside the project while Patterpad wrote beside it, so a project
369
+ * built in both had two bundles, and validate's staleness check never saw Patterpad's.
370
+ */
371
+ declare function defaultBundlePath(loaded: LoadedProject): string;
372
+ /**
373
+ * The output path for the compiled bundle (spec §11): the project's `export.bundle` (relative to the
374
+ * root, or absolute) if set, else `defaultBundlePath`. Where `patter export` and Patterpad's Build Bundle
375
+ * write, and where validate's staleness check looks.
268
376
  */
269
377
  declare function bundleOutputPath(loaded: LoadedProject): string;
378
+ /**
379
+ * A file exported beside the bundle (the playable page, the screenplay): the bundle's path with `ext` in
380
+ * place of its extension, whatever that is. Only a `.patterc` ending was ever swapped, so a bundle pinned
381
+ * as `build/game.json` (what a Unity TextAsset wants) was overwritten by the HTML. Throws when the result
382
+ * would be the bundle itself.
383
+ */
384
+ declare function besideBundlePath(loaded: LoadedProject, ext: string): string;
270
385
 
271
386
  /** Build a single self-contained playable HTML document for the loaded project. Pure: returns the file's
272
387
  * text; the caller writes it. One file to email anyone; it plays offline in any browser. */
@@ -287,8 +402,6 @@ interface WebExport {
287
402
  }
288
403
  declare function runExportWeb(loaded: LoadedProject): WebExport;
289
404
 
290
- declare function scanAudioStatus(loaded: LoadedProject): Map<string, string> | undefined;
291
-
292
405
  interface PlayOptions {
293
406
  /** Scene id to start at (defaults to the bundle's first scene). */
294
407
  scene?: string;
@@ -327,9 +440,18 @@ type PlayEvent = {
327
440
  | {
328
441
  type: "error";
329
442
  error: PlayError;
443
+ }
444
+ /** The run could not go on, and this is why: the engine stopped (a jump cycle with nothing to deliver,
445
+ * a jump to a target the bundle lacks), or a scripted choice named an option the choice did not offer.
446
+ * Always the last event, and the outcome is then "error". */
447
+ | {
448
+ type: "error";
449
+ fatal: true;
450
+ message: string;
330
451
  };
331
- /** "end" = the flow finished; "stalled" = a choice with no pickable option; "max-steps" = bound hit. */
332
- type PlayOutcome = "end" | "stalled" | "max-steps";
452
+ /** "end" = the flow finished; "max-steps" = bound hit; "error" = the run stopped, and the last event says
453
+ * why. (There is no "stalled": a choice with nothing to pick runs dry and play moves on.) */
454
+ type PlayOutcome = "end" | "max-steps" | "error";
333
455
  interface PlayResult {
334
456
  events: PlayEvent[];
335
457
  outcome: PlayOutcome;
@@ -339,6 +461,11 @@ interface PlayResult {
339
461
  * runtime. At each choice point it consumes the next scripted choice id, else
340
462
  * picks the first eligible option - so it always runs to an outcome. The whole
341
463
  * pipeline in one call: load -> export -> Engine playthrough.
464
+ *
465
+ * A run the engine stops part-way still returns its transcript: the throw is
466
+ * caught and becomes the last event, with outcome "error". It used to escape,
467
+ * so a playthrough that went wrong lost everything that led up to it, which is
468
+ * exactly what the author needs to see.
342
469
  */
343
470
  declare function runPlay(loaded: LoadedProject, opts?: PlayOptions): PlayResult;
344
471
  /** Render a play result as the CLI's readable transcript lines. */
@@ -385,7 +512,10 @@ interface CoverageBeat {
385
512
  * (one of several random picks is rare by design). Absent on a never-reached beat. */
386
513
  rare?: true;
387
514
  /** Set on a never-reached beat that is gated on a host-scope ref (`@world.x`) nothing writes and no
388
- * driver provides, i.e. it may just need an input, not be truly dead. Lists the offending refs. */
515
+ * driver provides, i.e. it may just need an input, not be truly dead. Lists the offending refs. The
516
+ * gate is the beat's own conditions and its ancestors', plus any ref that every jump into its block
517
+ * passes (a block reached only past a gated jump is gated too). Absent on a beat a branch sibling
518
+ * before it always wins over: no input can help that one. */
389
519
  needsInput?: string[];
390
520
  /** Set on a never-reached beat gated on a ref that IS written, but ONLY by content that was itself
391
521
  * never reached: the beat is dead at one remove, and the gate is not the real question. Names the
@@ -398,8 +528,13 @@ interface BlockedGate {
398
528
  /** The gating ref, at the granularity the condition reads it: `@world.alarm`, or `@world.mood:armed`
399
529
  * for a single flag of a flags property. */
400
530
  ref: string;
401
- /** Beat ids witnessing the writers: content that would have to play for this gate to be written. */
531
+ /** The writers: content that would have to run for this gate to be written. A writer with beats of its
532
+ * own is named by its beats; one without (a scene's entry effects, a snippet that only jumps, an option
533
+ * with only a prompt) by its own node id. */
402
534
  writers: string[];
535
+ /** The gate is on the way INTO the beat's block (a jump or call taken only past it), not on the beat
536
+ * itself: the place to look is the jump. */
537
+ onTheWayIn?: true;
403
538
  }
404
539
  /** A choice that ran DRY during the coverage run: at some point it had no takeable option and no eligible
405
540
  * fallback, so it fell through silently and the flow carried on past it. This is easy to author by
@@ -408,9 +543,11 @@ interface BlockedGate {
408
543
  /** A condition or effect that failed during the runs. The engine plays through these (a failing condition
409
544
  * counts as false, a failing effect is skipped), so without this list they would pass unseen. */
410
545
  interface ContentError {
411
- /** What failed: a condition, an effect, or a part of a condition scored for Best match. */
412
- kind: PlayError["kind"];
413
- /** The snippet, group, or option whose condition failed, or the snippet or scene owning the effect. */
546
+ /** What failed: a condition, an effect, or a part of a condition scored for Best match; or "stopped",
547
+ * when the engine threw and the run ended there (a jump cycle with nothing to deliver). */
548
+ kind: PlayError["kind"] | "stopped";
549
+ /** The snippet, group, or option whose condition failed, or the snippet or scene owning the effect. For
550
+ * "stopped", the scene the run was in when it stopped. */
414
551
  node: string;
415
552
  /** The scene the node lives in. */
416
553
  scene: string;
@@ -448,7 +585,9 @@ interface CoverageReport {
448
585
  };
449
586
  /** The reach % below which a reached beat counts as rare ({@link RARE_REACH_PCT}). */
450
587
  rareThresholdPct: number;
451
- /** How each run ended, for the summary header. */
588
+ /** How each run ended, for the summary header. `evalError` counts the runs the engine stopped, each
589
+ * with its cause in `contentErrors` (kind "stopped"). `stalled` is always 0 now (a choice with nothing
590
+ * to pick runs dry and play moves on), and stays only because Patterpad's Coverage window reads it. */
452
591
  termination: {
453
592
  ended: number;
454
593
  capped: number;
@@ -515,7 +654,9 @@ declare function renderCoverageText(report: CoverageReport, sceneName?: (id: str
515
654
  }): string[];
516
655
 
517
656
  /** Resolve where a flow starts: an explicit override, else the project's authored `start`, else `{}`
518
- * (the runtime's first-scene default). Shared by `runPlay` and `runCoverage`. */
657
+ * (the runtime's first-scene default). Shared by `runPlay` and `runCoverage`. A block named with no scene
658
+ * is looked for in every scene (see `findBlockAnywhere`), so it is never silently dropped for the
659
+ * project's own start; that search throws when the block is in no scene, or in more than one. */
519
660
  declare function resolveStart(loaded: LoadedProject, override?: {
520
661
  scene?: string;
521
662
  block?: string;
@@ -529,9 +670,11 @@ interface FormatResult {
529
670
  changed: boolean;
530
671
  /** The canonical content to commit - present only when `changed`. */
531
672
  write?: PlannedWrite;
673
+ /** Not Patter source (not a shard by its extension), so left alone. */
674
+ skipped?: boolean;
532
675
  }
533
676
  /** Compute the canonical form of each file. Returns planned writes; writes nothing. */
534
- declare function runFormat(files: string[]): FormatResult[];
677
+ declare function runFormat(paths: string[]): FormatResult[];
535
678
 
536
679
  /** Counts for the WRITTEN population (line + text + label beats). */
537
680
  interface WrittenCounts {
@@ -678,7 +821,8 @@ declare function extractLoc(loaded: LoadedProject, opts?: {
678
821
  locale?: string;
679
822
  }): LocCatalog;
680
823
  /**
681
- * Write a catalog's translations back. Produces the loc-shard writes (one per scene touched) and stamps
824
+ * Write a catalog's translations back. Throws, writing nothing, when the catalogue names a locale the
825
+ * project does not declare or a scene it does not have. Produces the loc-shard writes (one per scene touched) and stamps
682
826
  * `localisedAt[locale]` into the per-scene authoring shards (skipped when importing the default locale -
683
827
  * staleness is tracked against the source, not for it). Caller commits the returned writes.
684
828
  *
@@ -704,8 +848,9 @@ declare function poToCatalog(text: string): LocCatalog;
704
848
 
705
849
  /** Render the catalog as an .xlsx workbook: a sheet per scene, translator-facing columns. */
706
850
  declare function catalogToXlsx(catalog: LocCatalog): Promise<Buffer>;
707
- /** Parse an .xlsx workbook back into a catalog. Scene = sheet name; stale = Status "stale". `locale` is
708
- * not carried in the sheet, so the caller supplies it (--locale). */
851
+ /** Parse an .xlsx workbook back into a catalog. Scene = the hidden Scene column (else, for an older export,
852
+ * the sheet name); stale = Status "stale". `locale` is not carried in the sheet, so the caller supplies it
853
+ * (--locale). Throws when a scene is not a plain scene id (see `checkCatalogField`). */
709
854
  declare function xlsxToCatalog(buffer: Buffer): Promise<LocCatalog>;
710
855
 
711
856
  interface VoiceLine {
@@ -900,6 +1045,13 @@ declare function runInit(opts: InitOptions): InitResult;
900
1045
  * "switch VCS" path (which re-emits these for the newly-chosen VCS). No collision check - callers
901
1046
  * overwrite when switching. */
902
1047
  declare function vcsConfigWrites(dir: string, vcs: InitVcs | undefined, bundle?: BundlePosture): PlannedWrite[];
1048
+ /**
1049
+ * The bundle posture a project was set up with, read back from the files `init` wrote: the VCS's ignore
1050
+ * file naming `*.patterc`, else `vcs-setup.md` saying the project ignores it (svn keeps its ignore list in
1051
+ * a property). "commit" when neither says so. Switching VCS re-emits these files, and always writing
1052
+ * "commit" lost the ignore rule of a project made with `init --bundle ignore`.
1053
+ */
1054
+ declare function currentBundlePosture(dir: string, vcs: InitVcs | undefined): BundlePosture;
903
1055
 
904
1056
  /** The editor's caret context, so a search can float the current scene's hits (from the caret onwards) to
905
1057
  * the top. Omit for a context-free search (the CLI). */
@@ -985,6 +1137,8 @@ interface ReplaceOptions {
985
1137
  wholeWord?: boolean;
986
1138
  /** Restrict the replacement to a single beat id (the per-row "Replace this one"). */
987
1139
  onlyId?: string;
1140
+ /** The time stamped as each replaced string's `modifiedAt` (ISO 8601; default now). For tests. */
1141
+ now?: string;
988
1142
  }
989
1143
  /** One replaced string, for the preview / confirm list. */
990
1144
  interface ReplaceHit {
@@ -997,11 +1151,19 @@ interface ReplaceHit {
997
1151
  }
998
1152
  interface ReplacePlan {
999
1153
  hits: ReplaceHit[];
1000
- /** Shard writes the caller commits through the VC layer (one per touched scene). */
1154
+ /** Shard writes the caller commits through the VC layer: the string shards (one per touched scene) first,
1155
+ * then the authoring shards that carry their `modifiedAt` stamps (one per touched scene). */
1001
1156
  writes: PlannedWrite[];
1002
- /** The rewritten shard objects (index-aligned with `writes`), so the caller can swap them into its
1003
- * in-memory `LoadedProject.locales` after committing: keeping reads + the open scene current. */
1157
+ /** The rewritten string shard objects (index-aligned with the first `shards.length` of `writes`), so the
1158
+ * caller can swap them into its in-memory `LoadedProject.locales` after committing: keeping reads + the
1159
+ * open scene current. */
1004
1160
  shards: LocaleFile[];
1161
+ /** The stamped authoring shards (the rest of `writes`, in order), for the caller to swap into its
1162
+ * in-memory `LoadedProject.authoring` after committing, so a later edit builds on the stamps. */
1163
+ authoring: Array<{
1164
+ path: string;
1165
+ file: AuthoringFile;
1166
+ }>;
1005
1167
  /** Distinct scenes touched. */
1006
1168
  scenes: number;
1007
1169
  }
@@ -1114,9 +1276,12 @@ declare function runMerge(base: Obj, ours: Obj, theirs: Obj, opts?: {
1114
1276
  * (an empty result from `setIf` must not be reconsidered as "unhandled"). */
1115
1277
  declare const AUTHORING_HANDLED: Set<string>;
1116
1278
 
1117
- /** A document entry whose path escapes the target dir (rejected). */
1279
+ /** A document entry whose path escapes the target dir, or that names a dot-file or dot-folder (rejected). */
1118
1280
  declare class UnsafeEntryError extends Error {
1119
1281
  }
1282
+ /** Bytes that are not a `.patterpack` at all: not a zip, or a zip holding no project file. */
1283
+ declare class NotAPackError extends Error {
1284
+ }
1120
1285
  /** What unpacking a document plans: its shards, and its game scopes snapshot (empty for a pack with none,
1121
1286
  * which is every pack from before packs carried one). */
1122
1287
  interface UnpackResult {
@@ -1124,8 +1289,16 @@ interface UnpackResult {
1124
1289
  shards: PlannedWrite[];
1125
1290
  /** The game's scopes files, in `<targetDir>/game-scopes/`, where the new project finds them first. */
1126
1291
  scopes: PlannedWrite[];
1292
+ /** Entries that are none of a shard, a scopes file or a handoff record, by name: never written. */
1293
+ other: string[];
1127
1294
  }
1128
- /** Unpack a `.patterpack` document (zip bytes) into planned writes under `targetDir`. */
1295
+ /**
1296
+ * Unpack a `.patterpack` document (zip bytes) into planned writes under `targetDir`. Throws
1297
+ * `NotAPackError` for bytes that are not a pack, `UnsafeEntryError` for an entry bound outside the target
1298
+ * or naming a dot-file, and refuses a target that already holds a DIFFERENT project (its project id is
1299
+ * not the pack's): that leaves two project files, and every later load fails. Unpacking over the same
1300
+ * project, or into a folder with no project, is allowed.
1301
+ */
1129
1302
  declare function runUnpack(bytes: Buffer | Uint8Array, targetDir: string): Promise<UnpackResult>;
1130
1303
  /** One shard's outcome in a merge-unpack. */
1131
1304
  interface MergedShard {
@@ -1134,6 +1307,8 @@ interface MergedShard {
1134
1307
  /** Merge result, or undefined when the shard was ADDED (new file from the author). */
1135
1308
  result?: MergeResult;
1136
1309
  added: boolean;
1310
+ /** Whether there is a write for it: false when the merge leaves the shard saying what it already says. */
1311
+ changed: boolean;
1137
1312
  }
1138
1313
  /**
1139
1314
  * Whether the three documents in a merge agree about WHICH PROJECT they are.
@@ -1164,10 +1339,13 @@ interface ProvenanceCheck {
1164
1339
  interface UnpackMergeResult {
1165
1340
  shards: MergedShard[];
1166
1341
  /** Merged (and added) shard contents to write into the project, and `game.scopes.json` when the
1167
- * recipient changed the game's scopes (`gameScopes`). */
1342
+ * recipient changed the game's scopes (`gameScopes`). Only shards the merge changes. Commit `sidecars`
1343
+ * FIRST: a merged shard whose sidecar never landed is a conflict resolved to ours without a word. */
1168
1344
  writes: PlannedWrite[];
1169
1345
  /** `.patterconflict` sidecars for shards with conflicts. */
1170
1346
  sidecars: PlannedWrite[];
1347
+ /** The returned pack's entries that are none of a shard, a scopes file or a handoff record: never written. */
1348
+ other: string[];
1171
1349
  conflicts: number;
1172
1350
  warnings: number;
1173
1351
  /** Do the returned document, the base document and the target project agree on their project id? */
@@ -1445,4 +1623,4 @@ declare const REFUSE_MISSING_SHARE = 0.25;
1445
1623
  /** Plan an import. Reads the handoff record named in the document from the project. */
1446
1624
  declare function planEditableImport(loaded: LoadedProject, returned: ReturnedDoc, opts: ImportOptions): ImportPlan;
1447
1625
 
1448
- export { AUTHORING_HANDLED, type ApplyStats, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, type ReturnedDoc, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type SearchFocus, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applySuggestionDecisions, applyWrites, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, exportEditableScript, extractLoc, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, issueMarkerCodes, jsonToCatalog, leastReachedFirst, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, markerLength, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, planEditableImport, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, proposeCoverageDrivers, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudioStatus, sceneIdForShard, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, staleParts, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };
1626
+ export { AUDIO_MANIFEST_FILE, AUDIO_MANIFEST_SCHEMA, AUTHORING_HANDLED, type ApplyStats, type AudioEntry, type AudioRung, type AudioSnapshot, type BuildPlan, type BundlePosture, CONFLICT_SIDECAR, type CharacterReport, type Conflict, type ConflictKind, type ContentError, type CoverageAsyncHooks, type CoverageBeat, type CoverageHooks, type CoverageOptions, type CoverageOrder, type CoverageReport, type DecisionPlan, type DecisionResult, type DocumentManifest, type EditableScript, type EditableScriptOptions, type ExportOptions, type FormatResult, GAME_SCOPES_OWNER, type GameScopes, type GameScopesIssue, HANDOFF_DIR, type HandoffIssue, type HygieneIssue, type ImportOptions, type ImportPlan, type ImportProblem, type ImportReport, type InitOptions, type InitResult, type InitVcs, type LoadedProject, type LocCatalog, type LocEntry, type LocaleReport, type MergeFileType, type MergeResult, type MergedShard, NotAPackError, type OpenSuggestion, PATTER_SCOPES_FILE, PATTER_SCOPES_OWNER, type PinPlan, type PinnedName, type PlannedWrite, type PlayEvent, type PlayOptions, type PlayOutcome, type PlayResult, type ProvenanceCheck, RARE_REACH_PCT, REFUSE_MISSING_SHARE, type Random, type ReadBox, type ReadComment, type ReadItem, type ReadMarker, type ReadParagraph, type ReadText, type ReplaceHit, type ReplaceOptions, type ReplacePlan, type ReportData, type ReportTotals, type ResolveEntry, type ReturnedDoc, SHARD_EXTENSIONS, type SceneReport, type ScriptDoc, type ScriptElement, type SearchFocus, type SuggestionDecision, type TrackedChange, type UnpackMergeResult, type UnpackResult, UnsafeEntryError, UnsupportedMergeError, type ValidateResult, type VoiceLine, type VoiceScript, type VoicedCounts, type WebExport, type WrittenCounts, applyLoc, applySceneOrder, applySuggestionDecisions, applyWrites, audioFolderRungs, audioFoldersActive, audioManifest, audioManifestWrite, besideBundlePath, bundleOutputPath, catalogToJson, catalogToPo, catalogToXlsx, classesForChannel, compileLoaded, currentBundlePosture, defaultBundlePath, defaultGameScopesDir, detectMergeType, discoverGameScopes, drivableScopes, exportBlockers, exportEditableScript, extractLoc, findProjectFile, formatMarker, gameScopeTokens, gameScopesCatalogue, gameScopesSnapshot, handoffPath, handoffWrite, hostScopeTokens, issueMarkerCodes, jsonToCatalog, leastReachedFirst, listOpenSuggestions, listProjectTags, loadProject, loadProjectLanding, markerLength, newHandoffId, parseHandoff, patterScopesFile, patterScopesStale, patterScopesWrite, pickAudio, planBuild, planEditableImport, planPins, planReturnedWorld, planShareScopes, planWorldSave, poToCatalog, previewRegistry, proposeCoverageDrivers, reachabilityIssues, readEditableDocx, readGameScopes, readHandoff, readHandoffs, readMarker, renderCoverageText, renderPlay, renderReportText, resolveDocumentation, resolveStart, runCoverage, runCoverageAsync, runExport, runExportFull, runExportHtml, runExportWeb, runFormat, runInit, runMerge, runPack, runPlay, runPropertyUsage, runReplace, runReport, runReportXlsx, runResolve, runScriptDoc, runSearch, runStatusBrowse, runTagBrowse, runUnpack, runUnpackMerge, runValidate, runVoiceScript, scanAudio, scanAudioStatus, sceneIdForShard, scriptToDocx, scriptToPdf, serialiseHandoff, setCut, sidecarIssues, staleParts, vcsConfigWrites, voiceScriptToXlsx, worldSettingsScopes, xlsxToCatalog };