handoff-package.ts

PURPOSE

Bundles one weapon’s worth of VFX workbench artifacts into a markdown doc + machine-readable manifest JSON. The runtime-wiring session consumes the package to wire baked atlases, live shaders, anchors, and motion templates into the game’s render path. Implements 2026-04-19 parent spec §11 and 2026-04-20 §9.

OWNS

  • HandoffPackage interface — { markdownPath, markdown, manifest }.
  • HandoffManifest interface — weaponId, kitId, generatedAt (ISO), array of component entries (componentId, kind, category, atlasPath, liveShaderPath, anchors), plus pass-through relationships, previewMotion, entries from the kit.
  • buildHandoffPackage(kit, componentsById) — sole exported builder. Returns the full package.
  • Output path convention: docs/vfx-handoff/<weaponId>.md.

READS FROM

  • ./kit-schemaKitDef type (entries, relationships, previewMotion, forWeaponId, notes, name, id).
  • ./component-schemaComponentDef type (id, kind, category, anchors).
  • componentsById: Map<string, ComponentDef> — caller-supplied lookup; missing refs are flagged inline as **MISSING** rather than thrown.

PUSHES TO

Pure function — returns the package object. Caller is responsible for writing the markdown file and consuming the manifest. No filesystem I/O, no globals, no side effects beyond new Date().toISOString().

DOES NOT

  • Does NOT write files to disk.
  • Does NOT validate that referenced atlas PNGs or live-shader .ts files actually exist on disk.
  • Does NOT load components — caller resolves the componentsById map upstream.
  • Does NOT throw on missing components — emits a **MISSING** markdown stub instead so the runtime agent can resolve it.
  • Does NOT mutate the input kit or component map.
  • Does NOT touch the runtime — only generates handoff artifacts; runtime wiring is the consumer’s job.

Signals

Path conventions (hard-coded):

  • Baked atlas: src/starship-survivors/data/vfx/atlas/<category>/<id>.png
  • Live shader GLSL: src/starship-survivors/data/vfx/live-shaders/<id>.live.ts
  • Markdown output: docs/vfx-handoff/<weaponId>.md

Manifest component shape per entry:

  • kind: 'baked' | 'live_shader' — mutually exclusive with atlasPath / liveShaderPath (the other is null).
  • anchors: string[] — derived from Object.keys(comp.anchors ?? {}).

Entry points

  • buildHandoffPackage(kit: KitDef, componentsById: Map<string, ComponentDef>): HandoffPackage — the only export besides type interfaces.

Pattern notes

  • Dual output: human-readable markdown for the runtime agent to read + structured JSON manifest for programmatic consumption. The runtime-wiring session reads both.
  • Defensive on missing data, strict on shape: a missing ComponentDef produces a flagged stub; type contracts are enforced via TS.
  • Markdown built via line-array + .filter(Boolean).join('\n') to keep conditional sections (notes, params, missing-component blocks) readable.
  • Runtime wiring checklist inlined as a markdown checklist (- [ ]) covering initVfxComponents() boot wiring, reserveAtlasRegion / patchAtlasRegion for baked, initLiveShaderRuntimes() for live shaders, weapon-def kit reference, drawBullet archetype branch replacement with spriteBatch.add(...), Canvas 2D fallback retention.
  • Per-entry section displays kit-entry id, component id, kind, category, role, instance count (with fixed-count parenthetical if applicable), atlas or shader path, anchors, optional notes.
  • Relationships section falls back to italicized “no relationships declared” prose when empty.
  • Atlas / shader path appendices at the bottom of the markdown for quick scan; each falls back to italicized “none in this kit” when empty.

EXTRACT-CANDIDATE

Shared concepts that could be lifted across other workbench/handoff generators:

  • Markdown-via-array + filter(Boolean) pattern is repeated across workbench docs; a helper mdSection(lines: Array<string | false | null>) would deduplicate.
  • Path convention constants (atlas/<category>/<id>.png, live-shaders/<id>.live.ts, docs/vfx-handoff/<weaponId>.md) are duplicated wherever baked-vs-live components are consumed — candidate for a vfx-paths.ts module shared with vfx-component-runtime.ts and the atlas baker.
  • Missing-ref stub (**MISSING** — no <Type> found for this id) is a generic pattern; extract a missingRefMarkdown(kind, id) if other handoff generators land.
  • Manifest pass-through of kit fields (relationships, previewMotion, entries) suggests a typed KitHandoffView could be derived directly from KitDef via Pick.